Javascripts "setInterval()" used with jQuery animations results in a function queue up - javascript

What's happening
Okay, so I've made a function on my front page where some products are shown besides each other, fading out, changing products and fading back in every 5 seconds. The code looks like this:
var t;
$(document).ready(function(){
// Some pre-animation stuff, like loading the products etc.
// Initialize animation timer
t = setInterval("changeProducts();", 5000);
};
function changeProducts() {
// Fade out
$("#anfBox").fadeTo(200, 0.01, function() {
// Change products
// Fade back in
$("#anfBox").fadeTo(200, 1);
});
}
It all looks fine and runs as it should, except when i go to another window for a minute and then comes back, the changeProducts function is executed rapidly a few times (depending on how long i've been away). The products fade out, change fades in again, and then repeats instantly, where there should be a ~5 second delay.
What I've tried
So what I think I need to do is use something like clearInterval(t) when the focus is lost from the window, then re-initialize the timer when the window is re-entered, i just don't know how to do that, and i'm having a hard time finding anything useful on google.
I'm thinking maybe there's allso a way to run the animations even if the window is not in focus to avoid the function-queue.
I've also tried using setTimeout() instead but with no luck.
Any ideas on how to avoid the animation-queue is much appreciated.

With the latest browser updates lots of browsers now stop execution of code when current page or tab is not active. When you return that tab/window it executes all of queued actions and you see a rush of effect running each after.
Simply check effect queue before apply another effect, if queue's length is 0 then apply.
Try this (example);
var t;
function changeProducts() {
// Fade out
var $anfBox = $("#anfBox"),
queue = $anfBox.queue('fx');
if (queue && queue.length === 0) {
$anfBox.fadeTo(200, 0.01, function() {
// Change products
// Fade back in
$anfBox.fadeTo(200, 1);
});
}
}
$(document).ready(function() {
// Some pre-animation stuff, like loading the products etc.
// Initialize animation timer
t = setInterval(changeProducts, 5000);
});

Related

Replay jQuery function every 5 seconds

I want to replay my jquery function ChangeStats() every 5 seconds, it's currently doing sod all.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500);
setTimeout(function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
}, 500);
}
$(document).ready(function(){
setInterval(ChangeStats, 5000);
})();
Yes I have got the right class names.
No I haven't used underscores in my HTML.
I think it's something to do with my use of "find()", once the DOM has loaded and the function is set is it meant to traverse up the DOM tree instead of down?
EDIT:
Updated code, still not working.
HTML:
<span class="admin-stats-big-figures">%productCount%</span>
<span class="admin-stats-big-figures-hidden">hey</span>
Ok, I am going to go out on a limb and make several assumptions here; one is that you wish to cycle between two elements repeatedly, another is that you are using $(this) in the context of the window rather than a containing element. If either of these are incorrect then the following solution may not be suitable. However, let's give this a shot, eh?
1) You need to use setInterval rather than setTimeout to create a repeating call. You can of course "chain" your timeouts (ie: call the succeeding timeout from the code of the current timeout). This has some benefits in certain situations, but for now let's just assume you will use intervals rather than timeouts.
2) You call the find() jQuery method every time, which is a little unnecessary, especially if you will be repeating the actions so one idea would be to cache the lookup. If you are going to do that a custom object would be more suitable than separate global variables.
3) Some flexibility in terms of starting and stopping the animation could be provided. If we use a custom object as mentioned in (2) then that can easily be added.
4) You are using fadeIn and fadeOut, however if you wish the items to cycle then fadeToggle may be your best solution as it will simply allow you to do exactly that, toggle, without needing to check the current opacity state of the element.
5) Finally in my example I have provided a little extra "padding HTML" in order for the example to look good when run. Fading in jQuery will actually set the faded item to a CSS display of "none" which results in the content "jumping about" in this demo, so I have used some div's and a couple of HTML entity spaces to keep the formatting.
Ok, after all that here is the code..
// your custom animation object
var myAnim = {
// these will be cached variables used in the animation
elements : null,
interval : null,
// default values for fading and anim delays are set to allow them to be optional
delay : { fade: 500, anim: 200 },
// call the init() function in order to set the variables and trigger the animation
init : function(classNameOne, classNameTwo, fadeDelay, animDelay) {
this.elements = [$("."+classNameOne),$("."+classNameTwo)];
// if no fade and animation delays are provided (or if they are 0) the default ones are used
if (animDelay) this.delay.anim = animDelay;
if (fadeDelay) this.delay.fade= fadeDelay;
this.elements[0].fadeOut(function(){myAnim.start()});
},
// this is where the actual toggling happens, it uses the fadeToggle callback function to fade in/out one element once the previous fade has completed
update : function() {
this.elements[0].fadeToggle(this.delay.anim,function(el,delay){el.fadeToggle(delay)}(this.elements[1],this.delay.anim));
},
// the start() method allows you to (re)start the animation
start : function() {
if (this.interval) return; // do nothing if the animation is currently running
this.interval = setInterval(function(){myAnim.update()},this.delay.fade);
},
// and as you would expect the stop() stops it.
stop : function () {
if (!this.interval) return; // do nothing if the animation had already stopped
clearInterval(this.interval);
this.interval = null;
}
}
// this is the jQuery hook in order to run the animation the moment the document is ready
$(document).ready(
function(){
// the first two parameters are the two classnames of the elements
// the last two parameters are the delay between the animation repeating and the time taken for each animation (fade) to happen. The first one should always be bigger
myAnim.init("admin-stats-big-figures","admin-stats-big-figures-hidden",500,200);
}
);
OK, so now we need the HTML to compliment this (as I say I have added a little formatting):
<div><span class="admin-stats-big-figures">One</span> </div>
<div><span class="admin-stats-big-figures-hidden">Two</span> </div>
<hr/>
<input type="button" value="Start" onclick="myAnim.start()"/> | <input type="button" value="Stop" onclick="myAnim.stop()"/>
I have also provided buttons to stop/start the animation. You can see a working example at this JSFiddle - although the stop/start buttons are not working (presumably something specific to JSFiddle) they do work when in context though.
Here im gonna just replace your $(this). and maybe it'll work then.. also using callback.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500, function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
});
}
$(document).ready(function(){
setTimeout('ChangeStats()', 5000);
});

image source changing at the wrong time after a few minutes in a slide show

I'm a designer more than a programmer, but I'm trying to get the hang of javascript so I have more design options when it comes to web design beyond plugins. So here's the problem I'm having.
I have a page with 4 slide shows. Right now I have it so that when one of 4 buttons are clicked it will change the array it gets the image sources from and loop through the images every 4 sec.
At first the slideshows look fine and the change happens when the image is hidden, but after a couple minutes the source change falls out of time with the fading effects and happens when its visible. It also works fine to have the shows switch with no problem.
All the timing and variables get reset and the new parameter from the onClick-call switches which array is used. I tried one solution where I made the image change part it's own function and called it from other functions to control the timing more, but then for some reason clearTimeout stopped working to clear the loop and it would still play one instance of the loop at the same time as another.
Maybe someone can look at this code and see a simple fix for the timing issue.
i=0
var delay=4000
var fade=1000
var timer;
function play(showNum){
var showNum = showNum;
$("#show").stop(true,true)
newShow();
changeInfo(showNum);
$("#show").hide();
change(showNum);
}
function change(showNum){
$("#show").fadeIn(fade);
$("#show").attr("src",showNum[i]);
i = (i+1)% showNum.length;
$("#show").delay(delay-(fade*2)).fadeOut(fade);
timer=setTimeout (function(){change(showNum)},delay)
}
function newShow(){
clearTimeout(timer);
i=0;
}
Here is an update to the code since I posted this.
function play(showNum){
var showNum = showNum;
$("#show").stop(true,true);
newShow();
$("#show").hide();
changeInfo(showNum);
change(showNum);
$("#show").fadeIn(fade);
setTimeout(function(){loop(showNum);},fade*2);
}
function loop(showNum){
$("#show").fadeOut(fade);
setTimeout (function(){change(showNum);},fade);
$("#show").fadeIn(fade);
int=setTimeout (function(){loop(showNum);},delay);
}
function change(showNum){
$("#show").attr("src",showNum[i]);
i = (i+1)% showNum.length;
}
function newShow(){
clearTimeout(int);
i=0;
}
I don't get a problem with the timing anymore, but I do get a weird hiccup if I click on a new slideshow when it's on fadeOut where it changes image then changes back and then fades in with the next image of the previous slide show then fades out and starts the right slide show from there.

jQuery: prevent animation and timeout queuing?

I have probably a simple problem to solve but I don't know what's the right approach on that.
I have a div.notification bar on top of my website that is hidden by default. The bar does get an additional class of either success or warning that sets it to display:block; if needed.
So, there are two cases. Either an output message is rendered directly to the .notification bar on page-load (and it gets a class of success or warning right with it) OR the .notifcation bar is sliding down from top and is fed with json data.
Any way, the notification bar should always be visible for 10 seconds and than slide back up.
Therefore I wrote two functions that handle this bar.
var timing = 10000;
function notificationOutput(type, message) {
var note = $('.notification');
note.hide();
note.find('.message').html(message);
note.stop(true,true).slideDown().delay(timing).slideUp();
}
function notificationSlideUp(slideUp) {
var note = $('.notification');
if ( slideUp ) note.delay(timing).slideUp();
else note.stop(true,true).slideUp();
}
So the notificationOutput() function is triggered from various other functions that return json data and render that into the box.
And the notificationSlideUp() function is right now called on every page load because in my header I have this …
<script type="text/javascript">
$(document).ready(function(){
notificationSlideUp(true);
});
</script>
And there is a reason for that! Remember the case where the .notification is directly set to visible on page-load … e.g. when a user logs-in on my platform the .notification bar is immediately visibile and says "Welcome Username, you've successfully logged in".
I call the notifictaionSlideUp() function in my header to make sure the currently visible .notification bar will slideUp() again after 10 seconds.
And here occurs the problem … 
Somehow this causes the entire notifcation timing and sliding up and down to be "confused". So there must happen some queuing of the slide-functions and or of the delay() function, because without the note.stop(true,true) in the notificationOutput() function the notification wouldn't slideDown() immediately if it's triggered within the first 10 seconds after the page load. That is because the notificationSlideUp() function has already triggered the slideUp() and the delay() of the object.
And the same happens to the delay. If a .notification is put out within the first 10 seconds the delay timing isn't right because the delay counter already started on page-load.
Any idea how to solve that so that it always works?
Update:
var notificationTimer;
function notificationOutput(type, message) {
var note = $('.notification');
note.hide();
note.find('.message').html(message);
note.stop(true,true).slideDown();
clearTimeout(notificationTimer);
notificationTimer = setTimeout(function() {
note.stop(true,true).slideUp();
}, 10000);
}
function notificationSlideUp(slideUp) {
var note = $('.notification');
if ( slideUp ) {
clearTimeout(notificationTimer);
notificationTimer = setTimeout(function() {
note.stop(true,true).slideUp();
}, 10000);
} else {
note.stop(true,true).slideUp();
}
}
Unfortunately, jQuery's delay() function doesn't offer any method for canceling a delay the way setTimeout() does with clearTimeout(). I'd suggest replacing your delay()s with named setTimeout()s, and writing a condition to clearTimeout() for cases in which you need to cancel/trigger a queued animation right away.
http://api.jquery.com/delay/
The .delay() method is best for delaying between queued jQuery
effects. Because it is limited—it doesn't, for example, offer a way to
cancel the delay—.delay() is not a replacement for JavaScript's native
setTimeout function, which may be more appropriate for certain use
cases.
But you can use the following 'hint' - replace delay with some animation which does not change anything. For example with .animate({opacity:1},timing)

setInterval() with Jquery script

A quick thank you to those that have helped me so far with this script, you have all helped me enormously in learning some of the more elegant sides of javascript and jquery.
I have one final problem with this script, I am using setinterval() to cycle through an image changer, the JS/Jquerycode is as follows:
$(function() {
var rotateTimer = setInterval(rotateImg,15000);
$('#feature-links a').click(function() {
if (!$(this).hasClass('a-active')) {
clearInterval(rotateTimer);
switchToImg($(this).attr('class'));
}
});
function switchToImg(image) {
var $featureImage = $('#feature-image');
$featureImage.fadeOut(200, function() {
$featureImage.css('background-image', 'url(images/main_' + image + '.jpg)').fadeIn(200);
$('#feature-detail div').removeClass('d-active').filter('.d' + image).addClass('d-active');
});
$('#feature-links a').removeClass('a-active').filter('.' + image).addClass('a-active');
};
function rotateImg() {
var next = 'a' + (parseInt($('#feature-links a.a-active').attr('class').match(/[0-9]/))+parseInt(1));
if (!$('#feature-links a').hasClass(next))
next = 'a1';
switchToImg(next);
}
});
This script works on class names of <a> tags that allow a user to manually switch to an image. As well as this, rotateImg() is providing an automated image/text cycle every 15 seconds with the help of setInterval().
The problem I have is with setInterval() re-initialising once a user has clicked on a link manually.
In the .click function I clear the interval timer and then make a call to the switchToImg() function with the class name of the <a> tag that was clicked on passed as a variable.
I'm trying to work out how I can re-set the timer to avoid a user clicking on a link towards the end of the cycle and having it switch immediately to the next image.
I have researched building my own callback function in to switchToImg() so that once the function has completed the timer is reset, ideally I'd like this to be a longer time initially (30 seconds for example) but then settle back down into the 15 second clock. My research however has lead me to a load of different repositories that I'm having difficulty making head or tail of.
Any guidance as to how I can build this functionality into the script would be really appreacited. Thanks for your time. :)
I'm not 100% sure I follow what you're asking, but if what you're trying to do is to restart the interval timer after a delay after the user clicks, then you could do that like this:
$('#feature-links a').click(function() {
if (!$(this).hasClass('a-active')) {
clearInterval(rotateTimer);
switchToImg($(this).attr('class'));
setTimeout(function() {
rotateTimer = setInterval(rotateImg, 15*1000);
}, 15*1000);
}
});
You would be using a one-shot setTimeout() call to restart the interval timer after a 15 second delay. This would give you 15+15=30 seconds before the next image switched again after a click and then 15 seconds each time after that.
Not sure I understand the question correctly. But basically I get that you want to prevent the timer from happening after the user clicks. Shouldn't calling setInterval right after switchToImg do exactly that? It'll call switchToImg then after every 15 seconds from the click of the user.

A sticky situation for jQuery slideshow

I'm required to develop a slideshow (not an existing one) with jQuery. I was able to change picture with a function that I created named changePic (takes an image link). It incorporates the fading animation from the jQuery library.
For the slideshow I'm trying to use a while loop. It kind of works, except that it doesn't wait for the animation to finish.
How do I, a) wait for the animation to finish, b) delay the changing picture so it display the picture for a couple of seconds?
Also tried Settimeout, and it doesn't work.
Edit:
Basically changing image is like this:
function changePic(imglink){
var imgnode = document.getElementById("galleryimg");
$(imgnode).fadeTo(500, 0, function(){
$(imgnode).attr("src", imglink);
$(imgnode).fadeTo(1000, 1);
})
}
and the slideshow code is like this, but obviously it shouldn't.
function slideshow(gallerylinks){
var i=0;
while (i<gallerylinks.length){
changePic(gallerylinks[i]);
i++;
}
}
You could always try ditching the while loop, and going with a perpetually recursive function...
on the .animate, you could add a timeout function (at whatever interval) that calls the changePic function. As I have no idea what your code looks like, I will provide a fantastically generic outline.
/* array of imgUrls */
var imgUrls = new Array(); //populate it however
changePic(slideToShowIndex, fadeOutSpeed, fadeInSpeed, slideDelay)
{
$('#slideHolder').animate({ opacity: 0}, fadeOutSpeed , function(){
$('#slideHolder').attr('src', imgUrls[slideToShowIndex]);
$('#slideHolder').animate({ opacity: 1 }, fadeInSpeed, function() {
setTimeout(function() { changePic(slideToShowIndex+1, fadeOutSpeed, fadeInSpeed, slideDelay);}, slideDelay});
});
}});
}
$(document).ready(function() {
changePic(0, 5000, 5000, 10000);
});
This should (in theory) fade the image out, swap it with the new one, and fade it in (both taking 5 seconds) and then adding a delay to call itself with the next slide index in 10 seconds.
This is in no way perfect, but does outline the general idea. Since we have no idea what your code looks like, I can only assume your setTimeout was in the wrong spot. Doing it like this will make sure that the animation has finished before the timeout is set. This guarantees that the slide wont change until after the animation has changed.
of course you could always use a combination of the ':not(:animated)' selector and a setInterval to achieve much the same effect.
EDIT: made a slight change to stack the animations properly. The thoery behind this still works even with the OPs addition of code.
You could have provided more details or example code but have a look at stop() and delay() functions.

Categories

Resources