JavaScript

My variables are set to the last index in JavaScript

JavaScript is known to host some quirky behavior. Most of the time however, all it takes is getting familiar with it to understand what is really happening and how to work with it.

New and experienced users sometimes run into the issue where they don't understand why a value is not set to the one they expect. Let me show you an example.

I have a few buttons and I want identify them each with their index in the hierarchy.

<div id="buttons-holder">
    <button>Click me.</button>
    <br/>
    <button>Click me.</button>
    <br/>
    <button>Click me.</button>
    <br/>
    <button>Click me.</button>
    <br/>
    <button>Click me.</button>
</div>

When I click the first button, I want it to say "0", the second will say "1", the third will say "2" and so on. The simplest way to do this is to use a for loop and assign the index of each in the click function. Like this:

var div = document.getElementById("buttons-holder"),
    buttons = div.getElementsByTagName("button"),
    index,
    length = buttons.length;

for (index = 0;index < length;index++){
    buttons[index].onclick = function() {
        alert("I am index "+ index);
    };
}

This is straight forward. I select all the buttons, and in the for loop, I assign to each button click event the index variable. So what happens when I click on the last button?

Alert: I am index 5

When I click on the first button:

Alert: I am index 5

Every single button I click will show the same value. It can be confusing at first, but it makes sense.

index is a variable defined outside the scope of the onclick function. When we alert index inside the function, we are not accessing a copy of the variable, we are accessing index itself. After each loop, the variable index is incremented, thus the value is incremented.

When I click on a button, the onclick function is called. It sees the variable index and can't find a value inside the onclick scope. So it goes out to the scope where index is defined and sees that index is defined there and copies its value. Remember that when the loop is complete, the value of index is 5 because after each iteration we have index++. If I was to add at the end of my script:

index = 20;

Then every time I click a button will print I am index 20.

There is a solution

Instead of printing the value index, we can make a copy of it to be used inside the new scope.

var div = document.getElementById("buttons-holder"),
    buttons = div.getElementsByTagName("button"),
    index,
    length = buttons.length;

for (index = 0;index < length;index++){
    buttons[index].index = index;
    buttons[index].onclick = function() {
        alert("I am at index: "+this.index);
    };
}

Now outside the onclick function we assigned a value to the button object. This will be equal to the current value of index (not the variable itself) during the loop; this value will not be incremented. So if I change index, this.index will not be changed. (Note that this is the current object being clicked.)

Now when I click on the first button I will get:

Alert: I am at index 0

And any other button will return the correct index.

The key here is to remember that if the variable is defined outside the function, when that variable changes, it will affect its used inside the function.

Also one thing to keep in mind is that a for loop does not create a new scope.


Comments

Let's hear your thoughts

For my eyes only

There are no comments added yet.