14Feb

jQuery: fadeOut a lot, fadeIn a little: Deferred promises and synchronizing jquery.animate()

Posted by Elf Sternberg as javascript

Ever have the problem where you want to fade out a whole collection of stuff, and then show only one? A naive implementation looks like this:

$('.options .option').fadeOut('fast', function() {
    $('.options .option:eq(4)').fadeIn('fast')});

If you have five options, however, the callback gets called five times. Now, this is okay in the current example: fadeIn is idempotent, meaning that it doesn’t do any harm to call it multiple times as the target object is already faded in after the first; the rest result in no-ops. But what if you need to ensure that the callback gets called only once? What if you have multiple distinct animations that you need to ensure are complete before triggering the next set? Use an aggregated promise. To the best of my knowledge, jQuery.animate() still returns the jQuery root object and not a promise, but we can work around that, like so:

var promises = $('.options .option').map(function() {
    var dfd = $.Deferred();
    $(this).fadeOut('fast', dfd.resolve);
    return dfd.promise();
}).get();

$.when.apply(null, promises).then(function() {
    $(".options .option:eq(" + target + ")").fadeIn('fast');
});

Here, we return an array of promises, the resolution of each of which will be called when the fade out of each particular DOM object is done. when() all the promises are delivered, then the fadeIn is triggered. The call to get() at the end of the map() ensures that when() gets an array of promises, not an array of jQuery objects wrapping promises, which wouldn’t make sense.

This technique can be used to synchronize any multiple animations and ensure that all of them finish before proceeding to a new animation.

7 Responses to jQuery: fadeOut a lot, fadeIn a little: Deferred promises and synchronizing jquery.animate()

Jakob Løkke Madsen

February 17th, 2011 at 1:27 pm

Very cool example of using the new jQuery features!
Thanks for sharing.

Jurgen Welschen

February 28th, 2011 at 8:09 am

Thanks alot for this,

R'phael Spindel

August 5th, 2011 at 11:31 pm

Excellent use of some of more esoteric JQuery api’s.

Anton Stoychev

September 29th, 2011 at 11:05 am

I like it. Tasty deferred object from the oven :) .

Thank you, a very helpful post.

Adam Patridge

November 10th, 2011 at 2:03 pm

Not that it buys anything, but if anyone is drunk on the [mostly] one-line chaining Kool-Aid (guilty, sometimes), you can also build the mapped Deferred by passing the function directly to the constructor:

return $.Deferred(function (dfd) {
$(this).fadeOut(‘fast’, dfd.resolve);
}).promise();

Victor Widell

November 16th, 2011 at 5:42 pm

It’s more complicated than it needs to be. Perhaps jQuery was update with new functionality since you wrote it, but this is much clearer:

$blocks.each(function(i) {

$(this).delay(2000 * Math.random()).animate({
‘opacity’: 1
});
}).promise().done(mycallbackfunction);

http://jsfiddle.net/MP26y/

CMON

March 5th, 2012 at 4:36 pm

var siblings = $(element).siblings();
siblings.fadeOut(‘slow’)
.promise()
.done(function () {
$(element).fadeIn(‘slow’);
alert(‘promise’);
});

Comment Form

Recent Comments