Home > javascript, jquery > Sequentially chain your callbacks in jQuery - Three ways

Sequentially chain your callbacks in jQuery - Three ways

August 22nd, 2008

I had a challenge to get four divs to fade in sequentially.

Writing this out the long way is not really my favorite:

$("#vehicle1").fadeIn("slow", function(){
  $("#vehicle2").fadeIn("slow", function(){
    $("#vehicle3").fadeIn("slow", function(){
      $("#vehicle4").fadeIn("slow");
    });
  });
});

Ewwww. right?

After some conversation in the #jQuery IRC channel, I present twothree classier ways of accomplishing the same thing:

Self-executing callback chaining

(function showVehicle(elem){
  elem.fadeIn('slow',function(){
    $(this).next().length && showVehicle($(this).next()); 
  });
})( $("div.vehicle:first") );

Custom event triggering (via: ajpiano)

$('div.vehicle')
  .bind('showVehicle',function(e) {$(this).fadeIn('slow',function(){ 
    $(this).next().length && $(this).next().trigger("showVehicle"); 
  })})
  .eq(0)
    .trigger('showVehicle');
Update 2009.06.15: I'm incredibly impressed with temp01's solution in the comments. So that's my recommendation going forward:

Self-executing callback chain on an arbitrary jQuery object (via: temp01)

(function hidenext(jq){
    jq.eq(0).fadeOut("fast", function(){
        (jq=jq.slice(1)).length && hidenext(jq);
    });
})($('div#bodyContent a'))

Paul Irish javascript, jquery

  1. | #1

    I won't have you diss my way of doing it on IRC ;)

    Here it another way of doing a recursive call with jQuery:

     
    $(function(){
      recursiveFunction();
    });
     
     
    function recursiveFunction(){
      $('div:not(.recursiveRun):first').animate({width: '100px'},function(){
      $(this).addClass('recursiveRun');
        recursiveFunction();
      });
    }

    It might not be all that pretty and jQuery-like, but it still works.. :)

  2. temp01
    | #2

    Another way - works on any jquery object(not just on sibling elements):

    (function hidenext(jq){
        jq.eq(0).fadeOut("fast", function(){
            (jq=jq.slice(1)).length && hidenext(jq);
        });
    })($('div#bodyContent a'))
  3. | #3

    @temp01 That is very very nice, temp01. As far as flexibility is concerned, I think that's the nicest implementation here.

  4. | #4

    @Paul Irish
    I'm trying to develop a similar effect of the ted.com site where elements load sequentially over time. Can this solution work for that with times (500, 1000, 1200) instead of range settings (slow, medium, fast)?

  5. | #5

    @Trey Yah totally. Just replace the 'fast' shorthand with a millisecond value.

    You could also do something like:

    jq.eq(0).fadeIn([500,1000,1200][Math.floor(Math.random()*3)], function(){

    for some randomness.

  6. | #6

    i have just achieved the effect by a simple computer science 101 recursive function:

    $(document).ready({
    //say you have a div id=text with say 10 paragraphs... first hide them:
    $("#text p").hide();
     
    //then call the recursive function on the first p tag
    fadeUpR($("#text p:first"));
     
     
    });
     
    function fadeUpR(e){
     
    	$(e).fadeIn(500, function(){
    		if($(e).next("p").length != 0){
    			fadeUpR($(e).next("p"));
    		}
    	});
     
    }
  7. | #7

    another prettier way to do it, rather than waiting for the callback to fire, is to start each element fading up at a certain increment, say 200ms apart each, but have each element take longer than the triggering increment to complete its fade up. this is a prettier look.

    $(document).ready({
       //say you have a div id=text with say 10 paragraphs... first hide them:
       $("#text p").hide();
     
       for(i=1;i<=$("#text p").length; i++){
          setTimeout("fadeUp($('#text p:nth-child("+i+")'))", 100*i);
       }
     
    });
     
    function fadeUp(e){
       //note that this function could perform whatever you want on element e
       $(e).fadeIn(2000);
    }
  8. adolfo
    | #8

    @temp01
    really neat huh?

  9. | #9

    Here is one that I've been using. It fades in items sequentially then after a brief pause slowly fades them out again.

    		$(function(){
     
    			var delay = 100;
    			$("#menu div").each(function(){
    				var fade = $(this);
    				showItem(fade,delay);
    				delay += 250;
    			});
     
    		});
     
    		function showItem($o,n) { 
    		  setTimeout(function() {
    		     $o.fadeIn(1000);
    		     setTimeout(function(){
    			     $o.fadeOut(3000);    
    		     }, 3000);
    		  }, n); 
    		}
  10. | #10

    This one comes from NetTuts (http://tinyurl.com/ycm4bap), slightly modified to use a named function instead of arguments.callee (depricated):

    var elems = $('div.vehicle'), i = 0;
     
    // If using jQuery 1.3 or lower, you need to do $(elems[i++] || []) to avoid an "undefined" error
    (function fadePlz(){
      $(elems[i++]).fadeOut('slow', fadePlz);
    })();
  1. No trackbacks yet.

For code blocks, use <pre lang="javascript" escaped="true">. css and html4strict are also accepted.