qwertymk
qwertymk

Reputation: 35294

jQuery adding functions to the animation queue

The problem is that when I try doing multiple animations they all happen the same time.
Is there any way to have animations go one after another without using callbacks?

Here's what I want to do:

$('#a1').click(function() { $('#div1').hide(3000); });
$('#a2').click(function() { $('#div2').hide(3000); });
$('#a3').click(function() { $('#div3').show(3000); });

If you click on #a1 and then click on #a2 then #a3 before the first animation completes then it shouldn't start right away but instead wait until the animation queue is empty then start the next one.

Take this demo for example

I want to be able to click a1 then a2 then a3 one after the another and first have it hide the first div completely, then the second completely, and then show the third.

My example is overly simple and while this can be done with callbacks, my real problem can't so callbacks aren't an option.

In essence, if you click all three the animation should complete in 9 seconds.

This DEMO should alert ('took around 9 seconds to complete')

Upvotes: 9

Views: 2608

Answers (7)

Uwe
Uwe

Reputation: 515

I would use the animate() function as it comes with a complete function which is called when the animation finishes http://api.jquery.com/animate/.

So to use the jQuery doc example:

$('#clickme').click(function() {
  $('#book').animate({
   opacity: 0.25,
   left: '+=50',
   height: 'toggle'
}, 5000, function() {
// Animation complete this is where you call the next animation...
  });
});

function another_animation () {
  $('xyz').animate({
   opacity: 0.25,
   left: '+=50',
}5000, function() {
// Animation complete this is where you call the next animation

I think that this is the cleanest way...

Upvotes: 1

Ed I
Ed I

Reputation: 7358

Use .promise() to sidestep callbacks on show and hide:

The .promise() method returns a dynamically generated Promise that is resolved once all actions of a certain type bound to the collection, queued or not, have ended.

By default, type is "fx", which means the returned Promise is resolved when all animations of the selected elements have completed

Use .queue() to limit the number of animations resolved per promise (See also jsFiddle):

var promises = $({});

$('#a1').click(function() {
    promises.queue(function(next) {
        $('div').promise().done(function() {
            $('#div1').hide(3000);
            next();
        });
    });
    return false;
});
$('#a2').click(function() {
    promises.queue(function(next) {
        $('div').promise().done(function() {
            $('#div2').hide(3000);
            next();
        });
    });
    return false;
});
$('#a3').click(function() {
    promises.queue(function(next) {
        $('div').promise().done(function() {
            $('#div3').show(3000);
            next();
        });
    });
    return false;
});

Upvotes: 3

Jeffery To
Jeffery To

Reputation: 11936

Use .queue() on a common jQuery object:

var q = $({}); // this could also be a common parent, e.g. $('body')
$('#a1').click(function() {
    q.queue(function(next) {
        $('#div1').hide(3000, next);
    });
    return false;
});
$('#a2').click(function() {
    q.queue(function(next) {
        $('#div2').hide(3000, next);
    });
    return false;
});
$('#a3').click(function() {
    q.queue(function(next) {
        $('#div3').show(3000, next);
    });
    return false;
});​

Demo

Upvotes: 9

Sergei Zahharenko
Sergei Zahharenko

Reputation: 1524

Try to create some array with queue, and check if there is something in it, as callback for animation, and run it again if there is. I've played with your example a little.

check it out:

http://jsfiddle.net/acrashik/nqh6x/6/

var queue = {
    q: [],
    run: function(elem, type, time, recall) {
        if (queue.isRunning && !recall) {
            console.log('pushed: ' + elem + type + time);            
            queue.q.push({elem:elem, type:type, time:time});
        } else {
            console.log('running:' + elem);
            queue.isRunning = true;
            if (type=='hide') {
                $(elem).hide(time, function(){
                    queue.recall();
                })
            } else {
                $(elem).show(time, function(){
                    queue.recall();
                })             
            }
        }                    
    },
    recall: function(){
        console.log(queue.q.length);                    
        if (queue.q.length > 0) {
            queue.run(queue.q[0].elem, queue.q[0].type, queue.q[0].time, true);
            queue.q = queue.q.splice(1,queue.q.length);
        } else {
            queue.isRunning = false;
            queue.q = [];
        }
    },            
    isRunning: false
}
$('#a1').click(function() { queue.run('#div1','hide',2200) });
$('#a2').click(function() { queue.run('#div2','hide',2200) });
$('#a3').click(function() { queue.run('#div3','show',2200) });

Upvotes: 2

Luka
Luka

Reputation: 341

You could do something like this:

(function(){
    var col=(function(){
        var i=1;
        return function(n){alert("you clicked link nr " + n + ", total clicks: "+i++)};
    })();
    $('#a1').click(function(){col(1)});
    $('#a2').click(function(){col(2)});
    $('#a3').click(function(){col(3)});
})();

Im not gona write the entire code for you but that should give you a good idea of how to do it. Also none of the variables or functions are accessible from the global scope or any other.

Upvotes: 0

Piyush
Piyush

Reputation: 131

Use this $(element).promise().done(function () {...})

In your case

$('#a1').click(function () {
    $('#div1').hide(3000);
});
$('#a2').click(function () {
    $("#div1").promise().done(function () {
        $('#div2').hide(3000);
    });
});
$('#a3').click(function () {
    $("#div2").promise().done(function () {
        $('#div3').hide(3000);
    });
});

The next animation will be performed only when the previous animation on selected element is complete.

Upvotes: -2

nathancahill
nathancahill

Reputation: 10850

Add stop() before the animation:

$('#a1').click(function() { $('#div1').stop().hide(3000); });
$('#a2').click(function() { $('#div2').stop().hide(3000); });
$('#a3').click(function() { $('#div3').stop().show(3000); });

Upvotes: -1

Related Questions