Here is the example I'm looking to create...
Let's assume there are two divs, each containing some other HTML/Content (other divs). I would like to have one of these divs in the view on page load, and then after some number of seconds (let's say 5), scroll the second div onto the same place as the first div, and then repeating that process indefinitely until the user leaves the page.
The page and elements in question can be found at http://paysonfirstassembly.com/. I am attempting to animate the left sidebar with a class of dynamicPanel. There will be at least three of these divs, and they will nearly match up in content length.
I appreciate everybody's help. I am a very new client-side programmer and appreciate the respect that this community has with new developers.
Working demo of the following →
Here's a simple jQuery plugin I just made that will slide up the first div and place it at the end of the list. I've commented the code below to further explain so that this can just get you started and you can adjust it to your needs and learn about jQuery:
// the plugin declaration
$.fn.rotateEach = function ( opts ) {
// cache the element set
var $this = this,
// create some default options
defaults = {
delay: 5000
},
// pass the defaults to settings with any override options
settings = $.extend(defaults, opts),
// repeated rotation function
rotator = function ( $elems ) {
// slide up first element in set
$elems.eq(0).slideUp(500, function(){
// detach first element
var $eq0 = $elems.eq(0).detach();
// append it to wrapper
$elems.parent().append($eq0);
// fade it back in
$eq0.fadeIn();
// call rotator on reselection of elements
// since first element was moved to end
setTimeout(function(){ rotator( $( $elems.selector ) ); },
settings.delay);
});
};
// initial rotator call
setTimeout(function(){ rotator( $this ); }, settings.delay);
};
// invoke plugin
$('.dynPanelContent').rotateEach();
If you want to change the delay you can just pass it in as an option:
$('.dynPanelContent').rotateEach({ delay: 7500 }); // 7.5 seconds
Note: I also moved .dynPanelOpener and .dynPanelTitle within .dynPanelContent so that they're included in the animations.
See working example →
Related
My goal:
To enable a user to load a template (which contains preset jquery, html and css) to allow them to click one of five dots to trigger an animation on an image.
My issue:
When I load more than one of these templates to my page, my animation value (margin-left in this case) applies double the number of times that there is an instance of this template on the page. If my template is loaded twice, the margin-left sets to a value, jumps to the correct value, then back before finally setting on the correct value. This means that if I was to add 10 instances to the page, it would take 20 times as long to get that last value.
Before testing I thought that my code would be ok, as due to the context and .once()function, I believed it would only fire once.
All html and CSS are functioning as expected, it's just the jQuery is an issue.
My code:
(function ($) {
Drupal.behaviors.click_the_dots = {
attach: function (context, settings) {
$('.wrapper_class', context).once('click_the_dots', function () {
// Prevent other buttons from being clickable until the
// previous animation is complete.
var animationDone = false;
function clickDots(dotNum) {
$('.dot_class_num_' + dotNum).click(function () {
// Setup context, to keep animations to the container in which the dots exist.
var findElem = $(this).parent().parent().parent().find('.inner_wrapper');
// Prevent other buttons from being clickable until the
// previous animation is complete.
if (animationDone === false) {
animationDone = true;
// Find the visible image.
var animatingImage = findElem.find('.dot_class_num_active');
// Find the image that will be animating in.
var thisImageAnim = findElem.find('.dot_num_img_src_' + dotNum);
if (animatingImage.is(thisImageAnim)) {
// Can't click on the same dot again, until another dot is clicked.
animationDone = false;
return;
} else {
// Animate out the already visible image.
// Remove the visible image class as it's going to be hidden.
findElem.find('.dot_class_num_active').removeClass('dot_class_num_active');
// Animate it to hide to the left.
animatingImage.animate({
marginLeft: '-10%',
opacity: 0
}, 280, 'easeInOutQuad');
// Animate in the image associated with the dot click
// Set the image css to be further right in order to animate to left at 0.
thisImageAnim.css('margin-left', '10%').delay(200).animate({
marginLeft: '0',
opacity: 1
}, 300, 'easeInOutQuad', function () {
// Set the now visible image to the visible image.
thisImageAnim.addClass('dot_class_num_active');
}).promise().done(function () {
// Now allow the other dots to be clicked.
animationDone = false;
});
}
}
});
}
// For each of the five dots.
for (i = 1; i <= 5; i++) {
clickDots(i);
}
});}};})(jQuery);
I would like to add as many instances of this jQuery as required, but only have the function be looped through once. I'm not sure how to check if this has already been done, or how to ensure that once it has been done at least once, it shouldn't happen again.
:)
I figured out what my issue was - I was attaching the behaviour to the wrapper class of my content, i.e. $('.wrapper_class', context).once... - this meant that it attached the behaviour to each instance of this class, of which there could be many.
What I did was attach the behaviour to a higher parent element, of which I knew there would be only one instance. It attaches just once and the code works perfectly.
Thanks to the commenters above who helped me realise my issue!
I have a reveal.js presentation with approximately 300 slides. The purpose of this presentation is to cycle slides in "kiosk mode" on a monitor behind a conference booth.
To create a "kiosk mode" I've got:
Reveal.initialize({
controls: false, // hide the control arrows
progress: false, // hide the progress bar
history: false, // don't add each slide to browser history
loop: true, // loop back to the beginning after last slide
transition: fade, // fade between slides
autoSlide: 5000, // advance automatically after 5000 ms
});
This works very well, but I'd like to randomize the slides. The slides are currently just a list of 300 <section> tags in the index document - they aren't being pulled from anywhere external. Currently random: true isn't a configuration option in reveal.js.
The display order of fragments can be controlled with data-fragment-index. Is it possible to do something like that with sections? Is there a way to trick reveal.js into randomizing my slides?
My preference would be to shuffle them each time around - that is, to show slides 1-300 in random order, and then shuffle them, and show 1-300 again in a different random order. I would also be happy with just jumping to a random slide for each transition, though.
While Reveal itself does not have this functionality built in, it does let you set up event hooks to do actions when all the slides are loaded, this means JQUERY TO THE RESCUE!
You can combine Reveal's "All slides are ready" event with simple javascript to reorder all the sections, here's a simple PoC:
First import jQuery, I did this by adding it directly above the import for js/reveal.min.js:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Then, set up an event listener:
Reveal.addEventListener('ready', function(event) {
// Declare a function to randomize a jQuery list of elements
// see http://stackoverflow.com/a/11766418/472021 for details
$.fn.randomize = function(selector){
(selector ? this.find(selector) : this).parent().each(function(){
$(this).children(selector).sort(function(){
return Math.random() - 0.5;
}).detach().appendTo(this);
});
return this;
};
// call our new method on all sections inside of the main slides element.
$(".slides > section").randomize();
});
I put this right after declaring my Reveal settings and dependencies, but I'm pretty sure you can put it anywhere.
What this does is waits for all javascript, css, etc to load, manually reorders the slides in the DOM, then lets Reveal start off doing its thing. You should be able to combine this with all your other reveal settings since it's not doing anything disruptive to reveal itself.
Regarding the "shuffling them each time around" portion, the easiest way to do this would be to use another event listener, slidechanged. You could use this listener to check if the last slide has just been transitioned to, after which the next time slidechanged is called you could simply refresh the page.
You can do this with something like:
var wasLastPageHit = false;
Reveal.addEventListener('slidechanged', function(event) {
if (wasLastPageHit) {
window.location.reload();
}
if($(event.currentSlide).is(":last-child")) {
// The newly opened slide is the last one, set up a marker
// so the next time this method is called we can refresh.
wasLastPageHit = true;
}
});
As of reveal.js 3.3.0 there is now a built in helper function for randomizing slide order.
If you want the slide order to be random from the start use the shuffle config option:
Reveal.initialize({ shuffle: true });
If you want to manually tell reveal.js when to shuffle there's an API method:
Reveal.shuffle();
To shuffle the presentation after each finished loop you'll need to monitor slide changes to detect when we circle back to the first slide.
Reveal.addEventListener( 'slidechanged', function( event ) {
if( Reveal.isFirstSlide() ) {
// Randomize the order again
Reveal.shuffle();
// Navigate to the first slide according to the new order
Reveal.slide( 0, 0 );
}
} );
I have a function closeGeneralHelp which takes one parameter: idtag. The function is supposed to do the following:
a) slideUp the "Help" div,
b) slideDown a set of divs and then
c) scroll, jump, or otherwise go to a specific (but dynamically determined) other div of the set of divs that were exposed via the slideDown.
I have two classes of divs ("DivsToShow" and "DivToHide"). "DivsToShow" has a few other divs contained in it and each of those divs has a unique id (this is what idtag is).
Parts (a) and (b) work just fine. Scrolling to the specific (but dynamically determined) div seems to fail no matter what I try. Here is the code. Each /* */ comment block represents a separate, but failed (and flailing) attempt at making this work.
Any help on getting (c) to work? I am a JavaScript / jQuery infant. So any reasonably related advice / pointers are also appreciated.
function closeGeneralHelp(idtag){
$(".DivsToShow").slideDown("slow");
$(".DivToHide").slideUp("slow");
/* var divid = document.getElementById(idtag);
divid.style.display = 'block';
divid.scrollIntoView(true); */
/* document.getElementById(idtag).scrollIntoView();*/
/* $(window).scrollTo(idtag,800,{queue:true});*/
/* window.location.hash = idtag;*/
/* window.setTimeout(function() {window.scroll(0, findPos(idtag));}, 5);*/
}
Thank you!
You need a callback function, in this case after the slidedown is done:
$(".DivToHide").slideUp("slow");
$(".DivsToShow").slideDown("slow", function(){
var $ShownDiv = $(this);
// this is the callback of slidedown, after it is done, perform this:
$('html,body').scrollTop( $ShownDiv.scrollTop() );
// Or animated:
$('html,body').animate({scrollTop: $ShownDiv.scrollTop()}, 1000); // in ms
// Or more native JS
document.getElementById( $(ShownDiv)[0].id ).scrollIntoView();
});
If you have an old jQuery and cant upgrade, you might want to discard $("#someElement").scrollTop() in favor of $('#someElement').offset().top
I need to hide a div and, with this code it works fine:
var idObj = $(this).attr('key');
var valH = $(this).attr('hideval');
var valS = $(this).attr('showval');
if ($('div[name='+idObj+']').attr('isdisplay') == 'no') {
$('div[name='+idObj+']').children().show("slow");
$('div[name='+idObj+']').attr('isdisplay','yes');
var divTitle = $('div[name='+idObj+']').children().first();
var divArrow = $(this).children().first();
//.attr('src',prefixImg+valH);
//divTitle.show();
//divArrow.show();
$(this).children().first().attr('src',prefixImg+valH);
} else {
var divTitle = $('div[name='+idObj+']').children().first();
var divArrow = $('div[name='+idObj+']').children().last();
//.attr('src',prefixImg+valS);
$('div[name='+idObj+']').children().hide();
$('div[name='+idObj+']').attr('isdisplay','no');
divTitle.show();
divArrow.show();
$(this).children().first().attr('src',prefixImg+valS);
}
My div is hidden and the Title and arrows to reopen the div are shown. But if I try to use hide("slow") the divTitle and divArrow don't appear when my div is closed. Same problem using hide(1000).
Is there a difference between hide with and without "slow" parameter?
thanks,
Andrea
From the official site
The matched elements will be hidden immediately, with no animation. This is roughly equivalent to calling .css('display', 'none'), except that the value of the display property is saved in jQuery's data cache so that display can later be restored to its initial value. If an element has a display value of inline, then is hidden and shown, it will once again be displayed inline.
When a duration is provided, .hide() becomes an animation method. The .hide() method animates the width, height, and opacity of the matched elements simultaneously. When these properties reach 0, the display style property is set to none to ensure that the element no longer affects the layout of the page.
So, if hide is used without delay, it hides immediately without animating - eg, poof.
If it's used with time, it becomes animated, so it disapears over time.
For your problems, it is difficult to judge without the corresponding html code.
$(element).hide() hides an element instantly, where $(element).hide('slow') will animate its disappearance (slowly).
It looks like (though I'm not sure) you want to do stuff after the animation is finished. In that case, do something like this:
var that = this; // here to preserve scope for the block below
$('div[name='+idObj+']').children().hide('slow', function() {
// This stuff happens after the hide animation is done.
$('div[name='+idObj+']').attr('isdisplay','no');
divTitle.show();
divArrow.show();
$(that).children().first().attr('src',prefixImg+valS); // <= note "that" instead of "this"
});
According to the jQuery documentation
The strings 'fast' and 'slow' can be supplied to indicate durations of
200 and 600 milliseconds, respectively.
Also duration in milliseconds can be supplied to it..
I'm building my first js/jQuery site and I've run into a hiccup. I'm trying to use both jScrollpane (Kelvin Luck) and scrollTo (Ariel Flesler) plugins in one script. If I comment one out, the other works. Are they mutually exclusive? Do I need to unbind functionality out of jScrollpane to remove a 'scrollTo' call conflict or something? (I have no idea how to do that).
I'm using jScrollPane 2beta11 and scrollTo 1.4.2. Here's my stripped-down code using both:
// JavaScript Document
$(document).ready(function() {
//jScrollPane Init
$('#scrollingDiv').jScrollPane({
});
//scrollTo Refresh
$('div.scroll-pane').scrollTo( 0 );
$.scrollTo( 0 );
//Buttons
var $scrollDiv = $('#scrollingDiv');
var next = 1;
$('#but-rt').click(function(){
$scrollDiv.stop().scrollTo( 'li:eq(1)', 800 );
next = next + 1;
});
});
I'm aware that jScrollPane has it's own scrollTo functionality, but I need scrollTo's jQuery Object selectors in my particular project. I know I've got my HTML/CSS lined up fine because each function works as long as the other is commented out.
(By the way, I plan on using "next" variable to increment scrollTo button once I figure out how... not related to my problem tho.)
Any help is much appreciated. Let me know if there's anything else I need to supply. Thanks!
-Patrick
See how to use ScrollTo functionality of JscrollPane from the following url,
http://jscrollpane.kelvinluck.com/scroll_to.html
Hope this will help you...
I too was trying to use both jScrollpane (Kelvin Luck) and scrollTo (Ariel Flesler) plugins in one script. I've come across an easy solution which doesn't even require Ariel Flesler's AWESOME Script, if you don't necessarily require animated scrolling.
I wanted to be able to scroll to a label in a list of items when the page loads.
Here's how i did it:
$(function()
//Declare the ID or ClassName of the Scroll Element
//and the ID or ClassName of the label to scroll to
MyList = $('#MyElementID OR .MyElementClassName');
MyLabel = $('#MyElementID OR .MyElementClassName');
// Initiate the Scrollpane
MyScroll = $(MyList).jScrollPane();
// Connect to the jScrollPaneAPI
jScrollPaneAPI = MyScroll.data('jsp');
// Get position co-ordinates of the Label
var MyLabelPosition = $(MyLabel).position();
// Convert position co-ordinates to an Integer
MyLabelPosition = Math.abs(MyLabelPosition.top);
// Scroll to the Label (0-x, vertical scrolling) :)
jScrollPaneAPI.scrollTo(0, MyLabelPosition-3, true);
});
There's a small bug with the exact positioning when a list gets longer,
will post a fix asap...
They are mutually exclusive because jScrollPane removes the real scrolling and replaces it with complex boxes-in-boxes being moved relative to each other via JS.
This is how I successfully mixed them -- I had a horizontal list of thumbnails; this code scrolled the thumbnails to the center:
Activated jScrollPane:
specialScrolling = $('#scrollingpart').jScrollPane();
In my serialScroll code, where I usually would call
$('#scrollingpart').trigger('goto', [pos]);
in my case, inside my
onBefore:function(e, elem, $pane, $items, pos)
I put code like this:
jScrollPaneAPI = specialScrolling.data('jsp');
//get the api to manipulate the special scrolling are
scrollpos=(Math.abs(parseInt($('.jspPane').css('left'), 10)));
//get where we are currently scrolled -- since this is a negative number,
//get the absolute value
var position = $('#scrollingpart .oneitem').eq(pos).position();
//get the relative offset location of the item we are targetting --
//note "pos" which is the index number for the items that you can access
//in serialScroll's onBefore:function
itempos=Math.abs(position.left);
//get just the x-axis location -- your layout might be different
jScrollPaneAPI.scrollBy(itempos-scrollpos-480, 0, true);
//the 480 worked for my layout; the key is to subtract the 2 values as above
Hope this helps someone out there!
This doesn't cater for all use cases (it only handles scrollToY and scrollToElement), but offers a consistent API so you can just use $( /* ... */ ).scrollTo( /* number or selector */ ) and it will work on any element, jScrollPane or native.
You could extend the method condition to cater for all the other jScrollPane methods by inferring the value passed in target though.
(function scrollPaneScrollTo(){
// Save the original scrollTo function
var $defaultScrollTo = $.fn.scrollTo;
// Replace it with a wrapper which detects whether the element
// is an instance of jScrollPane or not
$.fn.scrollTo = function $scrollToWrapper( target ) {
var $element = $( this ),
jscroll = $element.data( 'jsp' ),
args = [].slice.call( arguments, 0 ),
method = typeof target === 'number' ? 'scrollToY' : 'scrollToElement';
if ( jscroll ) {
return jscroll[ method ].call( $element, target, true );
}
else {
return $defaultScrollTo.apply( $element, args );
}
};
}();