Scribblemacher Scribblemacher - 1 month ago 14
Javascript Question

Assigning the counter in a loop to a variable doesn't retain it's current value

I do not understand the behavior below.

I am expecting the script below to do the following:


  1. Get all elements with the class 'button'

  2. For each element with button, assign an onclick event

  3. When the user clicks a button, there's an alert with the current value of
    i
    when the onclick was assigned.



So I would expect clicking "Button 1" would alert "I am button 1".

Instead all 3 buttons alert "I am button 3". It looks like the
i
counter's value isn't be retained.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
<p class="button">Button 0</p>
<p class="button">Button 1</p>
<p class="button">Button 2</p>
<script>
var buttons = document.getElementsByClassName('button');
for (var i=0 ; i < buttons.length ; i++){
buttons[i].onclick = function(){
alert("I am button " + i);
};
}

</script>
</body>
</html>


Why does this happen? How can I achieve the desired behavior?

Answer

Try this. It is called Closures. It captures the variable i and because i is not deleted after the loop, it calls the event handlers with the current value of i. So you need a local variable, which you can get by executing function. So I create a function, pass a variable i to him, and in the function scope I have a variable, local to the function, param, which is new per iteration.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Test</title>
    </head>
    <body>
        <p class="button">Button 0</p>
        <p class="button">Button 1</p>
        <p class="button">Button 2</p>
        <script>
            var buttons = document.getElementsByClassName('button');
            for (var i=0 ; i < buttons.length ; i++){
                buttons[i].onclick = (function(param){
                    return function(){
                    alert("I am button " + param);
                      };
                })(i);
            }

        </script>
    </body>
</html>

Or you can use ES6 feature let

<!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title>Test</title>
        </head>
        <body>
            <p class="button">Button 0</p>
            <p class="button">Button 1</p>
            <p class="button">Button 2</p>
            <script>
                var buttons = document.getElementsByClassName('button');
                for (let i = 0 ; i < buttons.length ; i++){
                    buttons[i].onclick = function(){
                        alert("I am button " + i);
                     }
                }

            </script>
        </body>
    </html>