3

I'm trying to run each animation function one after the other instead of all at once.

This is what I've got so far:

   $(document).ready(function(){ 
        var bars = $('.bar');
        bars.each(function(){
            var widthpercent = $(this).attr("data-percent");
            $(this).fadeIn();
            $(this).animate({width:widthpercent},500);
        });
    });

I've tried using .delay() and setTimeout() in various combinations to no avail.

Could anyone point me in the right direction? Thank you!

Ian Nelson
  • 57,123
  • 20
  • 76
  • 103
user2979139
  • 143
  • 2
  • 12

3 Answers3

5

It sounds to me like you're looking for animate's complete function. You can write a recursive function to keep calling the function in the complete function until all the items have been animated. To simplify: every time one element is animated, a callback is fired that animates the next element. That is the purpose of the complete parameter, so I'm certain that is what you're looking for.

Here's an example you can adapt to your specific needs.

Live demo here (click).

var $divs = $('div');

function animate(element) {
  $(element).animate({height: '30px'}, {
    complete: function() {
      if (current < $divs.length-1) {
        ++current;
        animate($divs[current]);
      }
    }
  });
}

var current = 0;
animate($divs[current]);

Further, this same logic can be applied to your fadeIn. Just wrap fadeIn's callback around that logic, like this:

Live demo here (click).

var $divs = $('div');

function animate(element) {
  $(element).fadeIn(function() { //now the animation is a callback to the fadeIn
    $(element).animate({height: '70px'}, {
      complete: function() {
        if (current < $divs.length-1) {
          ++current;
          animate($divs[current]);
        }
      }
    });
  });
}

var current = 0;
animate($divs[current]);

And here's your code: live demo here (click).

$(document).ready(function(){ 

var $divs = $('.bar');

function animate(element) {
  $(element).fadeIn(function() { //you could unwrap this depending on what you're looking for
    var widthpercent = $(element).attr("data-percent");
    $(element).animate({
      width:widthpercent,
      duration: '500ms'
    }, {
      complete: function() {
        if (current < $divs.length-1) {
          ++current;
          animate($divs[current]);
        }
      }
    });
  }); //end fadeIn callback
}

var current = 0;
animate($divs[current]);

});
m59
  • 43,214
  • 14
  • 119
  • 136
  • 2
    this is the best solution, no timeouts needed – Vic Nov 25 '13 at 21:33
  • This answer is (should be) the correct solution. It is more clear to your intents and it doesn't rely on hoping the timeouts sync up with the animations. Let the animations tell you when they are done via a callback and call it a day! – rlemon Nov 25 '13 at 22:01
3

Try this:

var animate = function (el) {
    return function () {
        var widthpercent = el.data('percent');
        el.fadeIn();
        el.animate({
            width: widthpercent
        }, 500);
    }
}
var bars = $('.bar');
bars.each(function (index) {
    var $this = $(this);
    setTimeout(animate($this), index * 500);
});

Fiddle

Sergio
  • 28,539
  • 11
  • 85
  • 132
  • 2
    I personally wouldn't want to rely on the multiple `setTimeout` calls being perfectly in sync with the animations. Probably depends on how many there are though. – Blue Skies Nov 25 '13 at 21:15
  • 1
    @PSL: There already is a closure. The function passed to `.each()` creates a new variable scope with each invocation, and the function passed to `setTimeout` creates a closure of that scope. – Blue Skies Nov 25 '13 at 21:17
  • 1
    @BlueSkies oh yeah right..... But best way to do this would be to invoke a function on completion of animation for each element. – PSL Nov 25 '13 at 21:18
  • ...but if you did take that approach, you don't need to maintain the `time` variable. You can just use the counter given by `.each()`, and do `i * 500`. – Blue Skies Nov 25 '13 at 21:18
  • @PSL: I agree. I think that's a little safer than scheduling multiple timeouts, and hoping they stay in sync. – Blue Skies Nov 25 '13 at 21:19
  • @PSL, was this new version what you meant? – Sergio Nov 25 '13 at 21:33
  • Hit the nail on the head – user2979139 Nov 25 '13 at 21:43
  • @user2979139 I don't mean to be selfish here...but how is this the accepted answer? the `complete` parameter of `animate` IS the answer. That's why it exists. – m59 Nov 25 '13 at 21:53
  • I tend to agree with m59 here. The setTimeout is hacky at best. Why not let the animation tell you when it is complete instead of guessing? – rlemon Nov 25 '13 at 21:58
  • @m95 I couldn't get it working, and this works. That is why I have accepted the answer. Is there something i've missed – user2979139 Nov 25 '13 at 22:00
  • @user2979139 my answer explains it, uses it, and even has your code - all in good detail with live demos. – m59 Nov 25 '13 at 22:06
  • @m59 yes i've noticed your edit and too made changes as you'll find your answer has been marked as solution. Thank you. – user2979139 Nov 25 '13 at 22:10
1
$(document).ready(function(){ 
    var bars = $('.bar');
    bars.each(function(i){
        var widthpercent = $(this).attr("data-percent");
        $(this).delay(i*800).animate({width:widthpercent,opacity:1,},500);
    });
});

This will animate after delaying 800 * i milliseconds.

See this JSFiddle example.

Robbie Wxyz
  • 7,671
  • 2
  • 32
  • 47