setTimeout does bizarre things when executing in a tab that doesn't have focus
<blockquote>
  <p><strong>Possible Duplicate:</strong><br>
  <a href="http://stackoverflow.com/questions/5766263/run-settimeout-only-when-tab-is-active">Run setTimeout only when tab is active</a>  </p>
</blockquote>



<p>I have observed this happens in both Chrome and Firefox, and it manifests somewhat differently. It looks like when a tab is not in focus, the setTimeout will still execute, but the given dom elements that it should be doing things to <em>don't have</em> some of the properties that it should be taking into account.</p>

<p>For example, <a href="http://hobolobo.net/tale/2" rel="nofollow">check out this page</a>. There are two slideshows on panels 5 and 6. The functions that power this slideshow affect both the visibility and the position of the elements (to make them shrink to the center with the correct bounce easing without a wobble, I hacked together a stepper function). They work fine if you're looking at them, but if you tab over to another place, and then tab back to the slideshow, you will see that the setTimeout has advanced a bunch of times, but the images are all bunched up, as in the olden ones that should've popped out have not (as if they were not hidden or visible while the tab was out of focus), or they will miraculously transpose to the far right side of the stage (as if their coordinates were nonexistent or wrong while the browser window was not in focus). </p>

<p><strong>What is the best way to stop this from happening? To make the setTimeout not fire unless/until the browser window has focus?</strong> </p>

<p>One solution would be to use css3 transform instead of height and width, that would at least allow me to not have to deal with the coordinates of the elements while they're off screen, but it doesn't change the fact that setTimeout gets all bunched up and weird.</p>

<p>Here's the actual functions:</p>

<pre><code>// dom element goes out
function popOut(cssFilter, dur){
    var aPop = this;
    aPop.duration = dur;
    $(cssFilter+':visible').stop(false,true).each(function(){
        var my = this;
        my.X = $(this).position().left;
        my.Y = $(this).position().top;
        $(this).animate({'height': 0, 'width': 0}, {'duration': aPop.duration, 'easing': 'easeOutElastic', step: function(now, fx){
            if(fx.prop == 'height')     $(this).css('top', ((fx.start - now)/2) + my.Y);
            else if(fx.prop == 'width') $(this).css('left', ((fx.start - now)/2) + my.X);
        }, queue: true, complete: function(){
            $(this).css({'top': my.Y,'left': my.X, 'display': 'none'});
        } });        
    });
}

// dom element comes in
function popIn(cssFilter, dur){
    var aPop = this;
    aPop.duration = dur;
    $(cssFilter+':hidden').stop(false,true).each(function(){
        var my = this;
        my.H = this.height,
        my.W = this.width;
        $(this).css({ 'height': 0, 'width': 0, 'display': 'block'});
        my.X = $(this).position().left,
        my.Y = $(this).position().top;
        $(this).css({ 'left' :(my.X + my.W/2), 'top' : (my.Y + my.H/2)}).animate({'height': my.H, 'width': my.W}, { 'duration': aPop.duration, 'easing': 'easeOutElastic', step: function(now, fx){
            if(fx.prop == 'height')     $(this).css('top', ((fx.end - now)/2) + my.Y);
            else if(fx.prop == 'width') $(this).css('left', ((fx.end - now)/2) + my.X);
        }, queue: true, complete: function(){ 
            $(this).css({'top': my.Y, 'left': my.X, 'display': 'block'});
        } });        
    })
}

// dom element slideshow
function popSlideshower(classToCycle, pauseButtonPanelId){
    var thisPop = this;
    // the items that have the classToCycle class need to also have classes ploink1, ploink2, ploink3... ploinkn - to specify order of displaywhatchamacallit
    $('.'+classToCycle+":not(.ploink1)").hide();

    this.totCount = $('.'+classToCycle+':not(.also)').length; // the also class allows there to be multiple images with the same ploink number; the also ploinks will not go toward the total count
    this.currentIndex = 1;
    this.classOfCycler = classToCycle;
    this.currentlyPlaying = true;

    // prepend button to the given narrative block id
    $('#c_'+pauseButtonPanelId+' .narrative').prepend('&lt;p style="text-align: center;"&gt;&lt;a href="#" id="'+classToCycle+'-pauseButt" class="inlineButton"&gt;&lt;span&gt;Pause Slideshow&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;');

    $('#'+classToCycle+'-pauseButt').click(function(aClick){
        aClick.preventDefault();
        $(this).blur(); // removes the weird dotted outline in FF

        if(thisPop.currentlyPlaying) {
            $(this).html('&lt;span&gt;Continue&lt;/span&gt;');
            thisPop.currentlyPlaying = false;
        }
        else {
            $(this).html('&lt;span&gt;Pause&lt;/span&gt;');    
            thisPop.currentlyPlaying = true;
        }
    });

    this.popPloink = function(){
        if(this.currentlyPlaying){ // this method allows multiple slideshows to stay synchronized; the timeout never stops firing, even though the slideshow stops advancing
            var nextIndex = this.currentIndex == this.totCount ? 1 : this.currentIndex + 1;

            popOut("."+this.classOfCycler+".ploink"+this.currentIndex, 700);
            popIn("."+this.classOfCycler+".ploink"+nextIndex, 700);

            this.currentIndex = nextIndex;
        }
        setTimeout(function() { thisPop.popPloink(); }, 4000);
    }
    setTimeout(function() { thisPop.popPloink(); }, 4000);
}
</code></pre>
