Search code examples
javascriptscopedom-eventspass-by-value

How to add eventHandlers with local variables in a loop


I'm sure I knew this at one point, but I've been away for a while and I can't figure it out anymore.

Let's say I have a collection of buttons ("Button A", "Button B", "Button C", represented by a NodeList retrieved by .getElementsByClassName("button").

I now want to add an EventHandler to these that makes use of some local variables passed by value at the time the handler is created. For a minimal example, let's use:

for (var i = elems.length - 1; i >= 0; i--) {
    var inner = elems[i].innerHTML;
    elems[i].onclick = function(){window.alert("Clicked on "+inner+" which is element number "+i)};
}

The desired result here would be that, when clicking on Button B, I get

Clicked on Button B which is element number 1

However, all buttons yield

Clicked on Button A which is element number -1

I understand why this happens, but how would I need to change the code to achieve the former? In case this is relevant, in the final eventhandler the i will not be used by itself, but to reference elem[i] again, which is passed into the handler as an argument.


Solution

  • Use let to declare the variable. This will create a block scope for the variable and will retain the correct value trough out the loop:

    for (let i = elems.length - 1; i >= 0; i--) {
    

    Demo:

    let elems = document.querySelectorAll('button');
    for (let i = elems.length - 1; i >= 0; i--) {
      var inner = elems[i].innerHTML;
      elems[i].onclick = function(){window.alert("Clicked on "+inner+" which is element number "+i)};
    }
    <button type="button">Button A</button>
    <button type="button">Button B</button>
    <button type="button">Button C</button>