0

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.

Tobl
  • 671
  • 5
  • 17
  • Any specific reason for a decrementing loop rather than an incrementing one? – Krishna Prashatt Sep 04 '19 at 12:05
  • Was considered best practice when the order doesn't matter to only check the array length once. Not sure if it still is. – Tobl Sep 04 '19 at 12:30
  • 1
    Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – traktor Sep 04 '19 at 12:45

2 Answers2

1

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>
Mamun
  • 66,969
  • 9
  • 47
  • 59
0
var elems = document.getElementsByClassName('btn');
for (var i = elems.length - 1; i >= 0; i--) {
  (function(i) {
    var inner = elems[i].innerHTML;
    elems[i].onclick = function(){window.alert("Clicked on "+inner+" which is element number "+i)};
  })(i);
}

<button type="button" class="btn">Button A</button>
<button type="button" class="btn">Button B</button>
<button type="button" class="btn">Button C</button>
vaishali
  • 19
  • 2