Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the alert coming from my loop always return the last value, not each iteration value? [duplicate]

I have a some buttons, which are stored in an array. I then loop through that array to add a click event to each button. Each click alerts the value of i. I expect the values to be 1, 2, 3 and so on, but they always come back as one value, in case 3.

Can you explain why this happens and how to fix it?

Please see this a jsFiddle. Code below:

var theButtons = ['.button.one', '.button.two', '.button.three'];

for (i=0; i<theButtons.length; i++) {
    $(theButtons[i]).click(function () {
        alert(i); // always returns 3
    });
}

Please explain it as simply and clearly as you can - I'm somewhat of a beginner at Javascript and programming.

like image 703
shrewdbeans Avatar asked Jul 19 '13 15:07

shrewdbeans


People also ask

Why does JavaScript loop only use the last value?

You have some sort of for/next loop that then calls some asynchronous function. When the function runs, what you see when the code runs is that the last value of the loop index is the value that gets used in the function for every instance that it gets called.

Which loop is more efficient in JavaScript?

Comparison of performance of loops available in JavaScript : In case of multiple iterations of the loop, and where the size of array is too large, for loop is the preference as the fastest method of elements' iteration.

What does for loop mean in JavaScript?

A JavaScript for loop executes a block of code as long as a specified condition is true. JavaScript for loops take three arguments: initialization, condition, and increment. The condition expression is evaluated on every loop. A loop continues to run if the expression returns true.


3 Answers

By the time you click on the button i === 3. Need to pass the value of i into a closure:

for (var i = 0; i<theButtons.length; i++) { // do `var i` so the i is not global
    (function(index){
        $(theButtons[i]).on('click', function () {
           alert(index); // index === the value that was passed
        });
    })(i); // pass the value of i
}

Fiddle Demo: http://jsfiddle.net/maniator/fE55Y/3/

like image 71
Naftali Avatar answered Oct 21 '22 04:10

Naftali


Just use EACH method:

$( ".button" ).each(function( index ) {
    $(this).click( function(e){
        alert( index + ": " + $(this).text());
    });
});

Fiddle: http://jsfiddle.net/fE55Y/4/

Update:

Agreed that .each() is not needed for what's been asked. Here's the one without using .each() method.

$(".button").click(function () {
    alert("Button Index: " + $(this).index());
});
like image 39
Learner Avatar answered Oct 21 '22 05:10

Learner


Welcome to asynchronous programming and global variables!

The problem you are seeing here is because of the fact that i is declared globally in this case, in that is accessible from anywhere by anything.

So, what happens when your script is executed is:

  • Loop through the array of classnames
  • On each iteration, bind a click to the matching node, calling the anonymous function you provided

The problem here is that the function you provided is executed outside of your loop (e.g. when the click happens), so the value of i is whatever it was in the last iteration, in this case 2

like image 1
BenLanc Avatar answered Oct 21 '22 05:10

BenLanc