6

I’m having a setTimeout problem similar to this one. But that solution doesn't help me since I can’t use php in my file.

My site has a slider with a list of images that move every 8 seconds.However, when I have opened a few tabs in the browser and then switch back again, it goes nuts. The slider proceeds to move the images one after the other immediately without the 8 second timedelay.

I'm only seeing it in Chrome and the latest Firefox.

**EDIT: I checked with console.log() and the setTimeout returns the same number before and after the clearTimeout. Not sure why. Maybe that also has something to do with it? **

EDIT 2: I added a fiddle: http://jsfiddle.net/Rembrand/qHGAq/8/

The code looks something like:

spotlight: {
    i: 0,
   timeOutSpotlight: null,

   init: function()
   {
       $('#spotlight .controls a').click(function(e) {

           // do stuff here to count and move images

           // Don't follow the link
           e.preventDefault();

           // Clear timeout
           clearTimeout(spotlight.timeOutSpotlight);

           // Some stuff here to calculate next item

           // Call next spotlight in 8 seconds
           spotlight.timeOutSpotlight = setTimeout(function () {
                spotlight.animate(spotlight.i);
            }, 8000);
       });

       // Select first item
       $('#spotlight .controls a.next:first').trigger('click');
   },

   animate: function(i)
   {
       $('#spotlight .controls li:eq(' + (spotlight.i) + ') a.next').trigger('click');
   }
}
Community
  • 1
  • 1
Rembrand
  • 387
  • 2
  • 13
  • 1
    Does this only happen if you open new tabs of your same site, or if you open *any* new tabs and switch back? – namuol May 24 '11 at 15:29
  • Just random tabs of any site. – Rembrand May 24 '11 at 15:47
  • How intriguing. Some additional tests to help see what is going on - during the callback, if you console.log the datetime to see the real interval at which the code is hitting - is it 8 seconds? And, if you leave the page for X # of seconds, do you see the correct # of console logging events for the elapsed time given? – Matt May 24 '11 at 16:05
  • If you continue to struggle with this, I'd suggest replicating it with http://jsfiddle.net and posting the fiddle here. – namuol May 24 '11 at 18:28
  • Fiddle added: http://jsfiddle.net/Rembrand/qHGAq/3/ – Rembrand May 25 '11 at 06:52

7 Answers7

13

From the jQuery documentation:

Because of the nature of requestAnimationFrame(), you should never queue animations using a setInterval or setTimeout loop. In order to preserve CPU resources, browsers that support requestAnimationFrame will not update animations when the window/tab is not displayed. If you continue to queue animations via setInterval or setTimeout while animation is paused, all of the queued animations will begin playing when the window/tab regains focus. To avoid this potential problem, use the callback of your last animation in the loop, or append a function to the elements .queue() to set the timeout to start the next animation.

davydepauw
  • 1,790
  • 1
  • 12
  • 7
11

I finally found my answer and it’s not at all what I was expecting. It seems the culprit is jQuery’s .animate(), which I use to move the images in the slider.

I calculate and move my images positions with this:

$('.spotlight-inner')
    .animate(
        { left: scrollToVal },
        {duration: 'slow'}
    )
 ;

Now the problem seems to be that in some browsers, after you switch to a new tab and back, jQuery’s .animate() saves up the animations and fires them all at once. So I added a filter to prevent queueing. That solutions comes from CSS-Tricks.com :

$('.spotlight-inner')
    .filter(':not(:animated)')
    .animate(
        { left: scrollToVal },
        {duration: 'slow'}
    )
;

The first slide you see when you go back can act a little jumpy but it’s better than the superspeed carousel from before.

Fiddle with the full code here

Rembrand
  • 387
  • 2
  • 13
1

There is an easier way using the jquery animate queue property:

$(this).animate({
    left: '+=100'
}, {duration:500, queue:false});
Jon B
  • 2,444
  • 2
  • 18
  • 19
1

I don't know if this will help you, but it helped me with my slideshow. What I did was everytime I called an animation that was supposed to happen at a set interval because of the setTimeout, I called clearQueue() which would get rid of any other animations that had been set to happen. then i'd call the animation. That way when you come back to that tab, you don't have all these animations queued up and it goes crazy. at max you'll only have one set up.

So something like this:

       spotlight.timeOutSpotlight = setTimeout(function () {
            spotlight.clearQueue(); // get rid of other instances of the animation
            spotlight.animate(spotlight.i);
        }, 8000);

It may not work in all cases (depending on timing), but I hope that helps somebody!

  • The answer above me is WAY MORE correct, but can be difficult to do if you already have a ton of functions that are dependent on that timeout. – Grenard Madrigal Sep 11 '11 at 05:56
0

You must also think you use clearTimeout.

As you call setTimeout function it returns an ID you can save this ID in a variable like

timeoutID = setTimeout(function () {
                spotlight.animate(spotlight.i);
            }, 8000);

and before setting a new timeout you can call the function like

clearTimeout(timeoutID)
user160820
  • 14,866
  • 22
  • 67
  • 94
0

My suspicion is that the browser queues input events like 'click' but only fires them when the tab where the event occurs actually has focus.

Perhaps you should try calling your click callbacks directly instead of using trigger('click').

Something like this:

spotlight: {
    i: 0,
   timeOutSpotlight: null,
   clickFunc: function(element) {

       // do stuff here to count and move images

       // Clear timeout
       clearTimeout(spotlight.timeOutSpotlight);

       // Some stuff here to calculate next item

       // Call next spotlight in 8 seconds
       spotlight.timeOutSpotlight = setTimeout(function () {
            spotlight.animate(spotlight.i);
       }, 8000);
   },

   init: function()
   {

       $('#spotlight .controls a').click(function (e) {

           // Don't follow the link
           e.preventDefault();

           spotlight.clickFunc(this);
       });

       // Select first item
       spotlight.clickFunc($('#spotlight .controls a.next:first'));
   },

   animate: function(i)
   {
       var element = $('#spotlight .controls li:eq('+spotlight.i+') a.next');
       spotlight.clickFunc(element);
   }
}
namuol
  • 9,816
  • 6
  • 41
  • 54
  • I tried your code (thanks for the write-up by the way) but the result is still the same unfortunately. You may be on to something with the firing of the clicks though. I just noticed the slider goes back to its normal tempo after a while. It does look like the browser is saving up the clicks until you focus back on the tab and then it runs them all at once. – Rembrand May 24 '11 at 18:09
  • I just checked with console.log() and the setTimeout returns the same number before and after the clearTimeout. Not sure why. Maybe that also has something to do with it? – Rembrand May 24 '11 at 18:13
  • Judging by the number of logs, the console does continue logging these messages after I leave the tab and come back. – Rembrand May 24 '11 at 18:20
0

What version of jQuery are you running? Apparently this problem was 'fixed' for version 1.6.3 - they reverted the change that caused this to happen. Discussions here and here.

Though this issue will likely have to be addressed in the future, it seems as though we're off the hook for now.

Brendan
  • 868
  • 1
  • 16
  • 38
  • That's good news. It was an earlier version of jQuery but we should keep davydepauw’s answer in mind, in case it ever comes back. – Rembrand Sep 21 '11 at 11:22