Scott
Scott

Reputation: 21890

Jquery: How to fire same event on multiple objects in sequence rather than all at once

I've got a page of divs which I want to reorder on page load, moving divs of a specific class to the top of the page.

For the most part I have this working using insertAfter(). However, I'd like to fire the events in sequence so there's more of a stair-step to the animation.

You can see where I'm at currently with this jsFiddle.

HTML:

<div class="lists">
    <div class="marker"></div>
    <div class="row0">Item</div>
    <div class="row0">Item</div>
    <div class="row0">Item</div>
    <div class="row99">Item</div>
    <div class="row0">Item</div>
    <div class="row0">Item</div>
    <div class="row0">Item</div>
    <div class="row99">Item</div>
    <div class="row0">Item</div>
    <div class="row99">Item</div>
    <div class="row0">Item</div>

</div>

Using this jquery it functions.

$('.row99').each(function(i,e){
    $(e).delay(500).slideUp(1000, function(){
        $(this).insertAfter('.marker').slideDown(1000);
        });
    });

As you'll see, on page load specific divs are slid up, inserted at the top, then slid down. However, all the special divs fire at the same time.

Is there a way to make these items fire one after another rather than all at the same time?

Secondary is you'll see that when the divs slide down, then are in reverse order (10-8-4). This isn't a big deal, but if there's a way to retain the basic order (4-8-10) rather than reversing it, I'd love that as well.

Upvotes: 1

Views: 237

Answers (3)

zzzzBov
zzzzBov

Reputation: 179176

Rather than rely on specific timings, I recommend using queueing via jQuery's .queue() method:

fiddle:
$('.row99').each(function(i,e){
    $('.lists').queue(function (n) {
        $(e).slideUp(1000)
            .queue(function (next) {
                $(this).insertAfter('.marker');
                next();
            }).slideDown(1000, n);
    });
});

The way this works is that each animation is queued in order on a shared object. In this example, I queued everything on the .lists element.

The n and next parameters are the .dequeue() callbacks that tell the queue to move on to the next item in the queue.

Within $('.lists').queue(... you can see that I chose to use another queue call rather than passing a complete callback to slideUp. This is because the callback parameters cause "Arrow Code" especially when there are callbacks within callbacks.

The advantage to coding this way is that there is no reliance on the duration of the animations. You can feel free to change the durations in a single location and the queueing behavior will continue to work as expected.


There are many ways of fixing the ordering issue. A simple one is to add a class to the moved elements and position all subsequent elements after the last moved element (same thing outlined by adeneo):

fiddle:
$('.row99').each(function(i,e){
    $('.lists').queue(function (n) {
        $(e).slideUp(1000)
            .queue(function (n) {
                $(this).insertAfter($('.marker, .moved').last()).addClass('moved');
                n();
            }).slideDown(1000, n);
    });
});

Upvotes: 1

jfriend00
jfriend00

Reputation: 707786

Here's a way to use completion functions to make them run one after the other without really changing your main logic much at all:

(function() {
    var items = $(".row99");
    var index = 0;
    function next() {
        if (index < items.length) {
            items.eq(index++).slideUp(1000, function() {
                $(this).insertAfter('.marker').slideDown(1000, next);
            });
        }
    }
    next();
})();

Working demo: http://jsfiddle.net/jfriend00/ZfVw5/

In a nutshell, you use exactly the same code you were using, but instead of running the operation in a .each() loop, you run one at a time advancing a counter each time so you can fetch the next one in the list. The completion function from the ending .slideDown() starts the next iteration. The whole thing finishes when your counter points past the end of your list of objects.

Upvotes: 2

adeneo
adeneo

Reputation: 318302

Just stack the delays

$('.row99').each(function(i,e){
    $(e).delay((i+1)*1000).slideUp(1000, function(){
        $(this).insertAfter('.marker').slideDown(1000);
    });
});

FIDDLE

A simple way to keep the order would be to add a class, get the last instance of that class, and insertAfter that instead

$('.row99').each(function (i, e) {
    $(e).delay((i + 1) * 1000).slideUp(1000, function () {
        var n = i!==0 ? $('.moved:last') : $('.marker');
        $(this).insertAfter(n).addClass('moved').slideDown(1000);
    });
});

FIDDLE

Upvotes: 2

Related Questions