Search code examples
jqueryanimationdelaysettimeout

Using setTimeout To Delay A jQuery Animation


I'm working on some out transitions on image when a user presses the button to move to the next image. When there are multiple images next to each other that are very narrow, those images will all transition at the same time. So I check the width on the images and add the thin ones to an array and then run the transition on each object in the array. I wanted to create a little delay between the animation of each image in the array so I'm trying to run my jQuery.animate after a 1 second timeout.

Here's the ways I've tried to get the timeout working without success:

1.

for (i=0; i < set.length; i++) {
    if (i != 0) {
        setTimeout(function() { set[i].transitionOut($('#gallery'), 562) }, 100);
    } else {
        set[i].transitionOut( $('#gallery'), 562 );
    }
}

2.

for (i=0; i < set.length; i++) {
    if (i != 0) {
        function tempTransition() {
            set[i].transitionOut( $('#gallery'), 562 );
        }
        setTimeout(tempTransition, 100);
    } else {
        set[i].transitionOut( $('#gallery'), 562 );
    }
}

3.

for (i=0; i < set.length; i++) {
    if (i != 0) {
        setTimeout('function() { set[i].transitionOut($("#gallery"), 562) }', 100);
    } else {
        set[i].transitionOut( $('#gallery'), 562 );
    }
}

transitionOut():

jQuery.fn.transitionOut = function(parent, height) {
    this.animate({
        height: '0',
        top: height + 'px'
    }, function() {
        $(this).remove();
    });
}

I got it working using CMS's closure example. But, now I'm running into a new problem. The transition only happens with the second image. When there is three images it will go animate image 1, delay, animate image 2, animate image 3. There's no delay between 2 and 3.

Here's the new code:

for (i=0; i < set.length; i++) {
    (function(i) {
        if (i != 0) {

            function tempTransition() {
                set[i].transitionOut( $('#gallery'), 562 );
            }
            setTimeout(tempTransition, 200);
        } else {
            set[i].transitionOut( $('#gallery'), 562 );
        }
    })(i);
}

Solution

  • By looking at your code I think you have yet another closure problem, perhaps the most common problem I've seen when people work with loops and nested functions.

    The i variable which your setTimeout callback function refer, is the same (because JavaScript has only function-scope, not block-scope), and by the time those functions are executed asynchronously, the loop have already finished, and the i variable will contain set.length - 1 for all cases where setTimeout is used.

    As usual, try to capture the i variable using another closure:

    for (var i=0; i < set.length; i++) {
      (function (i) {
        if (i != 0) {
            setTimeout(function() { set[i].transitionOut($('#gallery'), 562) }, 100);
        } else {
            set[i].transitionOut( $('#gallery'), 562 );
        }
      })(i);
    }