0

I'm trying to pass an index of element and slideUp each list item content with delay

here is my code

    for(var i = 1; i <= $("#colContainer li").length ; i++) {
        var el = $("#colContainer li:nth-child(" + i + ") .colContent");

        var delay = function() {
            slide(el);
        };
        setTimeout(delay, 10);
        function slide(el){
            el.slideUp();
        };
    };

but every time just the last one slides up

what I expect is they slideUp from index 1 to the end with delay

I also tried this

    index = $(this).parent("li").index();
    for(var i = 1; i <= $("#colContainer li").length ; i++) {
        (function(i) {
            var el = $("#colContainer li:nth-child(" + i + ") .colContent");

            var delay = function() {
            slide(el);
            };
            setTimeout(delay, 10);
            function slide(el){
            el.slideUp();
            };
        })(i);
    };

but they all slide at once, i want index 1 slide, after that index 2 and ...

IS THERE ANY WAY WITH FOR LOOP ??

zEn feeLo
  • 1,877
  • 4
  • 25
  • 45
  • 3
    Standard mistake with variables and closures (the timeout function *is* a closure, but here we really another function scope to create a *new* variable binding) - see http://stackoverflow.com/questions/3273210/javascript-closures-variable-scope-question?rq=1 , http://stackoverflow.com/questions/341723/event-handlers-inside-a-javascript-loop-need-a-closure The thing take away: **there is *only one* outer variable called `el`**. – user2246674 Jun 20 '13 at 23:35

4 Answers4

7

This is because var el is scoped to the function block, not the loop block.

Try something like this:

for( var i=1; ......) { (function(i) {
    var el = ...
    // rest of your code, unchanged
})(i); }
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • @KamranAsgari If your updated question is what you tried, you're missing the `(` right before the new `function` – jcsanyi Jun 20 '13 at 23:43
  • yes they all slide at Once , I want first one slide after that the second one and... – zEn feeLo Jun 20 '13 at 23:46
  • 2
    Then you've got a different problem - you solved the "last-one-only" problem, and now your only problem is you should be increasing the `setTimeout` delay for each one. Maybe use `10*i` instead of `10`? – jcsanyi Jun 20 '13 at 23:51
  • @Mamran, 10 milliseconds isn't detectible by the human eye. and multiplying it by 2 or 3 doesn't change much... – Yevgeny Simkin Jun 20 '13 at 23:55
  • @jcsanyi if u post ur answer I will accept, u said before the others – zEn feeLo Jun 21 '13 at 00:08
  • 1
    @KamranAsgari thanks, but I've got to run, and don't have time to write it up properly. Go ahead and give it to somebody else. – jcsanyi Jun 21 '13 at 00:13
6

You need a closure to scope the value of el for each iteration of the loop.

for(var i = 1; i <= $("#colContainer li").length ; i++) {
  var el = $("#colContainer li:nth-child(" + i + ") .colContent");
  (function(el) {
    setTimeout(function(){
        el.slideUp();
    },10);
  })(el);
}

However this will still cause them to all animate at the same time which if that is the desired result, you could just do it all in one step with jQuery. If you want them to animate one at a time you can do this:

for(var i = 1; i <= $("#colContainer li").length ; i++) {
  (function(i) {
    var el = $("#colContainer li:nth-child(" + i + ") .colContent");
    setTimeout(function(){
        el.slideUp();
    }, i * 10);
  })(i);
}
Alex D
  • 189
  • 4
  • they all slide together, I Updated the question , I want first one slide, after that the second one after that the third ... – zEn feeLo Jun 20 '13 at 23:52
  • In that case scope the i instead and use i*10 for the timeout. I've updated my answer to make them slide individually. – Alex D Jun 20 '13 at 23:54
2

Did you want them to be queued or for a 10 millisecond delay before they all slide up?

Do you require the for loop?

Wouldn't the following do the latter?

setTimeout(function() {
   $("#colContainer li .colContent").slideUp();
}, 10);

Queued slide example:

(function slideContent(index) {
   $("#colContainer li:nth-child(" + index + ") .colContent").slideUp();
   if ($("#colContainer li:nth-child(" + (index + 1) + ") .colContent").length == 1) {
      setTimeout(function() { slideContent(index + 1); }, 250);
   }
})(1);
JonWarnerNet
  • 1,112
  • 9
  • 19
  • @KamranAsgari, right, which is what I figured, therefore you should consider what I'm saying in my answer, because even though it was downvoted (no idea why) it correctly identifies the problem... your slides aren't going to slide up in sequence, they're all going up at the same time. (though 10 milliseconds is basically nothing, so, you can't even tell the difference!) change it to 250ms and you'll see what I mean. – Yevgeny Simkin Jun 20 '13 at 23:53
  • so there is no way with for loop ? u say ? – zEn feeLo Jun 20 '13 at 23:58
  • 2
    for chaining (one after the other) I would recommend Alex D's 2nd solution: http://stackoverflow.com/a/17225554/1441046 – JonWarnerNet Jun 20 '13 at 23:58
  • 1
    no, you can use a for loop and alter the delay as the others have said. I've edited my question with more useful information. I wouldn't use a for loop, however, I'd chain them to call successively as they execute, but that's entirely a matter of personal taste. – Yevgeny Simkin Jun 21 '13 at 00:01
  • updated with an alternative to Alex D's solution for a chained, does a queued slideUp animation. Added a bit more of a delay to help illustrate the point better. – JonWarnerNet Jun 21 '13 at 00:12
0

Unless your intention is to have them all animate at the same time, you can't set them up in a loop this way. If you do, they're all executed (almost) simultaneously and as you say, you'll only actually see the last one.

You need to trigger each successive one from the completion of the previous one. Chain them together with callbacks.

delay should set up the next setTimeout. Then you'll get the result you're after.

EDIT Given the other answers here, I'll add that you'll probably want to increase your pause time from 10ms to something like 100 and then use the *i solution that the others have suggested. Multiplying 10ms by i isn't going to get you a whole lot in the way of noticeable delay. I'd start with 100ms and if that's too jerky move down from there in increments of 10ms till you have an animation that makes you happy.

Yevgeny Simkin
  • 27,946
  • 39
  • 137
  • 236
  • I did it before, with for example 4 CONSECUTIVE setTimeout but I want to do it with for loop – zEn feeLo Jun 20 '13 at 23:34
  • maybe I'm not understanding your question then. It seems to me that you're trying to cue up all your animations at once. – Yevgeny Simkin Jun 20 '13 at 23:35
  • 1
    @DonRhummy, The closure will solve his scoping issue, but I don't see that it will help with preventing all his animations from firing at once... unless I'm overlooking something. Maybe the OP wants all the items to slide up simultaneously, in which case Kolink's solution is indeed correct. – Yevgeny Simkin Jun 20 '13 at 23:38