Dong Liang Dong Liang - 4 months ago 24
Javascript Question

JavaScript button addEventListener



window.onload = function() {
for(i = 1; i < 21; i++) {
var button = document.createElement("button");
var text = document.createTextNode(i);
button.appendChild(text);
button.addEventListener("click", function() {
alert(text.nodeValue);
});
var body = document.getElementsByTagName("body")[0];
body.appendChild(button);
}
}





I want to add 20 buttons to the page arranged in a row, with indexes 1, 2, 3, ..., 20. When clicking on button should alert its index. For example, clicking on the first button(the leftmost) should alert 1.
I want to do these with pure javaScript and only javaScript without writing any html. For the code I wrote, they will alert 20 for all 20 buttons even though text inside button is correct.
This is what it alerted when I clicked button with index 1
My question is why do they alert 20 for all 20 buttons instead of correct indexes, how can I make them show correct indexes?

Update:



window.onload = function() {
for(i = 1; i < 21; i++) {
var button = document.createElement("button");
var text = document.createTextNode(i);
button.appendChild(text);
button.addEventListener("click", function() {
alert(button.innerText);
});
var body = document.getElementsByTagName("body")[0];
body.appendChild(button);
}
}





This won't work either, same issue.

Answer

The problem is that your event listener actually fires sometime LATER (when a click actually happens) after the for loop is completely done. As such, the text variable is set to it's last value which will be the last button you create. Thus, you only see the last text value.

You can fix it by just referencing the actual item that was clicked rather than the text variable.

    window.onload = function() {
        for(var i = 1; i < 21; i++) {
            var button = document.createElement("button");
            var text = document.createTextNode(i);
            button.appendChild(text);
            button.addEventListener("click", function() {
               // get the clicked on button with this
               alert(this.firstChild.nodeValue); 
            });
            document.body.appendChild(button);
        }    
    }

Or, by creating a closure that gives each iteration of the loop it's own lasting variables:

    window.onload = function() {
        for(var i = 1; i < 21; i++) {
          // create a separate closure for each iteration of the for loop
          (function() {
            var button = document.createElement("button");
            var text = document.createTextNode(i);
            button.appendChild(text);
            button.addEventListener("click", function() {
               alert(text.nodeValue); 
            });
            document.body.appendChild(button);
          })();
        }    
    }

FYI, for future reference, you can get the body element with document.body rather than having to find it like you were doing.