WhilseySoon
WhilseySoon

Reputation: 322

eq:() doesn't work properly

I have a code:

        $("#button").click(function () {
            for (var i = 0; i < 4; i++) {
                setTimeout(function () {
                    $(".rows:eq("+i+")").css("background-color", "blue");
                },500);
            }
        });

For some reason only the fifth element gets background-color grey. What is wrong?

Upvotes: 0

Views: 44

Answers (2)

guest271314
guest271314

Reputation: 1

Try using .delay() , .queue() , recursively call click handler with incremented .index() of .row as parameter after initially setting .eq() to event.data : 0

// `0` : `event.data`
$("#button").click(0, function re(event) {
  var i = typeof event.data === "number" ? event.data : event;
  $(".row").eq(i).delay(500).queue(function() {
    $(this).css("background-color", "blue");
    if ($(this).index(".row") < 3) {
      re(i + 1)
    }
  })
})
.row {
  color:orange;
  width:36px;
  padding:8px;
  font-weight:bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button id="button">click</button>
<span class="row">0</span>
<span class="row">1</span>
<span class="row">2</span>
<span class="row">3</span>
<span class="row">4</span>

Upvotes: 0

Jeroen Peeters
Jeroen Peeters

Reputation: 1998

It has to do with the scoping of the i variable. It is bound to the for loop, not to the function invoked by setTimeout. Basically, when the delayed function is invoked, i is already increment to 4. In order to bind the current loop value of i, call another function. i is placed on the function stack and therefore the value if preserved.

It depends what kind of behavior your're looking for, but if you want to change the css of all elements at once @Josh's comment is the best.

$("#button").click(function () {
   setTimeout(function (){
     for (var i = 0; i < 4; i++) {
       $(".rows:eq("+i+")").css("background-color", "blue");
     }
   ,500);
});

Otherwise try:

bindItoFunc = function (i) {
  return function (){
    $(".rows:eq("+i+")").css("background-color", "blue");
  };
}
$("#button").click(function () {
  for (var i = 0; i < 4; i++) {
     setTimeout(bindItoFunc(i),500);
   }
});

Maybe a more elegant solution is to bind i as the this value of the function.

bindItoFunc = function () {
  $(".rows:eq("+this+")").css("background-color", "blue");
}
$("#button").click(function () {
  for (var i = 0; i < 4; i++) {
     setTimeout(bindItoFunc.bind(i),500);
   }
});

Question asker indicated that the background should change in order. In that case, change the waitMs based on i.

bindItoFunc = function () {
  $(".rows:eq("+this+")").css("background-color", "blue");
}
$("#button").click(function () {
  for (var i = 0; i < 4; i++) {
     setTimeout(bindItoFunc.bind(i),500+(100*i));
   }
});

Upvotes: 2

Related Questions