jQuery: deferred objects and animated slideshows

Today I had to write my weekly post on the Html.it blog and I suddenly start mumbling on the recent possibilities offered by jQuery's deferred objects. When we deal with content sliders we usually have to perform two main tasks: sliding and animating. If you take a look at one of my past works, namely BTMagazine, you'll surely notice that each slide has a lot of effects and animations. You'd probably be worried by knowing that each animation set has been created by using a long chain of nested callback functions that span on several lines. In short, the code works but it's a mess. Deferred objects, instead, allow us to create all the effects we need without having to use callback functions. Let's see how.

In the traditional, chained model, you use callbacks like this:

$('#slideshow-wrapper').animate({
 left: - $('div.slide').eq(index).position().left
}, 1000, function() {

 $('div.slide').eq(index).
 find('div.desc').fadeIn(1000);

});

If you're lucky, two or three additional animations is enough, but sometimes (just like in my case) you have to deal with dozens nested effects. Here's when deferred objects come into play.

Deferred objects allow us to concatenate actions, but in a radically different way: each code section has its own life which is contained within the Deferred wrapper.

For example:

var Slider = new function() {
    
    var timer = null,
        index = 0,
        wrapper = $('#slideshow-wrapper'),
        slides = $('div.slide', wrapper);
    
    var slide = function() {
        
        timer = setInterval(function() {
            
            index++;
            
            if(index == slides.length) {
             
                index = 0;
               
                $('div.desc', wrapper).hide(); 
                
            }
            
            $.Deferred(function(def) {
                
                def.pipe(function() {
                  
                    return wrapper.animate({
                        left: - slides.eq(index).position().left
                       
                    }, 1000); 
                    
                }).pipe(function() {
                   
                   return slides.eq(index).
                      find('div.desc').fadeIn(1000); 
                    
                });
                
            }).resolve();
            
        }, 2000);
        
    };
    
    this.init = function() {
        
      slide();  
        
    };
        
    
    
}();
  
  
$(function() {
   
  Slider.init();
   
}); 

There are two separate actions in the following code, not a chain:

$.Deferred(function(def) {
                
                def.pipe(function() {
                  
                    return wrapper.animate({
                        left: - slides.eq(index).position().left
                       
                    }, 1000); 
                    
                }).pipe(function() {
                   
                   return slides.eq(index).
                      find('div.desc').fadeIn(1000); 
                    
                });
                
}).resolve();

You can also write two separate functions or methods to be executed within each call to the pipe() method. This practice allows us to write clear, concise, organized and compact code.

You can see the live example on JSFiddle.

This entry was posted in by Gabriele Romanato. Bookmark the permalink.

Leave a Reply

Note: Only a member of this blog may post a comment.