0

With the following code I want the li elements to fade in one by one, but the all fade in together:

$('li').each(function () {
    $(this).delay(1000).fadeIn(1000);
});

Can anyone explain why they do not fade in one by one?

5
  • Well, you're assigning the same 1 second delay to each. Commented Mar 4, 2016 at 15:59
  • For the same reason doing a setTimeout in a loop with all the same delay happen all at the same time. Commented Mar 4, 2016 at 15:59
  • api.jquery.com/delay "Using the standard effects queue, we can, for example, set an 800-millisecond delay between the .slideUp() and .fadeIn() of <div id="foo">: $( "#foo" ).slideUp( 300 ).delay( 800 ).fadeIn( 400 ); Only for anaimation, not a sleep to js Commented Mar 4, 2016 at 16:00
  • and, each li gets it's own queue in this case. Commented Mar 4, 2016 at 16:01
  • @Jason210 did you want "1s delay, 1s fade, 1s delay, 1s fade, ...", or just "fade, fade, fade..." with 1s between each one? Commented Mar 4, 2016 at 16:12

4 Answers 4

2

The .delay call doesn't stop the loop from carrying on immediately, so all of the delays and animations will start (pretty much) at once.

The most trivial solution is to stagger the delay by the current index number:

$('li').each(function(index) {
    $(this).delay(1000 * index).fadeIn(1000);
});

A better solution would use a pseudo-recursive loop and the "completion callback" of the animation to trigger the next iteration:

var els = $('li').get();
(function loop() {
    if (els.length) {
        var el = els.shift();
        $(el).fadeIn(1000, loop);
    }
})();

This method is generally preferable because it ensures that the next fade-in cannot possibly start until the previous has finished, and also avoids creating multiple parallel delay / fade queues (one per element) since the 2nd animation isn't queued until the first has finished.

Sign up to request clarification or add additional context in comments.

8 Comments

delay(1000) is not needed any more in your loop version, I think
@fengd good point, but does depend on the OP's intent. The original code (if it had worked synchronously) would have done delay, fade, delay, fade...
Yes, it does help to see it done another way. Thank you.
I did it like this: $(this).delay(1000 * index).fadeIn(1000);
@Jason210 right, so that means there's no initial delay, and that the other delays (in theory) just arrange for the next fade to start immediately after the next. I'll update accordingly.
|
1

I guess this is because you basically telling each li to wait 1 sec and to fade in. So that's what they do :)

Right now, your code is similar to:

$('li').delay(1000).fadeIn(1000);

Try something like that :

var delay = 0;
$('li').each(function () {
    $(this).delay(delay).fadeIn(1000);
    delay += 1000;
});

Or, as Alnitak suggest, a cleaner way is to use the current index provided by $.each() :

$('li').each(function (index) {
    // index will return the loop index starting to 0
    $(this).delay(index*1000).fadeIn(1000);
});

8 Comments

you do know that .each provides the current iteration count?
yes, I just wanted to make something more readable to @Jason210
NB: the delay starts at 1000, not 0
Not sure about that, usually, the first animation starts on page load (it would make no sense to wait 1 second for the first anim).
perhaps not, but that's what the OP's code would do if it weren't async
|
0

cause each is not delayed. Every delay is applied almost at the same time.

you may want to try use the complete part do fadeIn the next element

.fadeIn( [duration ] [, complete ] )

jQuery api doc

Comments

0

This is a misunderstanding of jQuery.each. It doesn't mean do it for one, then wait for that to finish before moving on, for that you'd need to use a promise.

Instead, try changing your delay to a multiple of the index of each LI in the array, e.g.:

$('li').each(function(index) {
  $(this).delay((index + 1) * 1000).fadeIn(1000);
});

Since array indices always start from 0, and 0 * 1000 = 0, I've added 1 to the index before multiplying by 1000 to ensure the first one happens after 1 second, the second after 2 seconds and so on.

If you don't want a 1s delay to the first li fading in, then it's simply:

$('li').each(function(index) {
  $(this).delay(index * 1000).fadeIn(1000);
});

3 Comments

no need for promises here - animations provide a "completion callback"
@Alnitak I know they're not needed in this instance, I was highlighting that to achieve that sort of behaviour in a loop (in general terms) would require the use of promises. Teach a man to fish and all that ;-)
You can't write (traditional) loops using promises.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.