views:

1042

answers:

3

I'm trying to set up a loop where an animation runs a certain number of times, and a function is run before each iteration of the animation. The timing ends up being off, though -- it runs the callback n times, then runs the animation n times. For example:

for (var i=0;i<3;i++) {
  console.log(i);
  $('#blerg').animate({top:'+=50px'},75,'linear', function(){log('animated')});
}

outputs

0
1
2
animated
animated
animated

I ran into this problem with scriptaculous before I switched to jquery, and discovered a "beforeSetup" animation callback. Is there a jquery equivalent?

+2  A: 

The animation is asynchronous. So the loops runs through pretty quickly, starting off three animations and outputting 1, 2 and 3. After a while the animations complete and output animated x 3. That would explain your output.

How about some recursion?

do_animation(max_runs, total_runs) {
   log();
   if (total_runs < max_runs) {
       $(foo).animate(..., do_animation(max_runs, ++total_runs));
    }
}

do_animation(3, 0);

Give that a try and let me know how it runs.

MDCore
+1  A: 

You could also try utilising the Queue function.

http://docs.jquery.com/Effects/queue#callback

Internally I recall animate uses the same execution queue, so this should work in theory ( untested ).

/* Safe Namespace + Onload : I do this everywhere */
jQuery(function($){ 
 /* Calling this once instead of many times will save 
    a lot of wasted calls to document.getElementById + processing garbage 
  */
 var blerg = $('#blerg');
 var addStep = function( i )
 {
     blerg.queue(function(){ 
        console.log(i);
        $(this).dequeue();
     }); 
     blerg.animate({top:'+=50px'},75,'linear'); 
     /* In theory, this works like the callback does */
     blerg.queue(function(){
       log('animated');
       $(this).dequeue();
     });
 };

 for (var i=0;i<3;i++) 
 {
  /* I call a function here, because that way you don't need to worry 
     about the fact 'i' will reference the last value of 'i' when the code 
     gets around to executing. */
    addStep(i); 
 }
});


Kent, I don't quite understand why you need to explicitly put the callback in the queue. Not that you're wrong -- it doesn't work if the callback is an argument to animate() -- but I'm just curious.

Its not necessary for the second case, but I thought it made for more consistent and somewhat neater code if one was going to endeavor to do more things in the callback phase ( for instance, another animation ).

Then you would just put the next animate call after the second blerg.queue,

Not to mention it has the added benefit of creating a bit of programmatic nicety in that the entire execution sequence is defined before it needs to be run, making the execution largely linear.

So this makes the code still "how you think it works" and makes it still run "how you need it to work" without needing to worry about the whole asynchronicity of it all. ( Which makes for less buggy and more maintainable code )

Kent Fredric
A: 

Both of those solutions worked like a charm! Thanks, MDCore and Kent!

Kent, I don't quite understand why you need to explicitly put the callback in the queue. Not that you're wrong -- it doesn't work if the callback is an argument to animate() -- but I'm just curious.

Altay