Cannot get the index out of a JS collection when click event is triggered

This should be a simple question with a simple answer but it’s eluding me. When I click on one of the items an unordered list, I don’t seem to be able to get its index value reported back to me.

I can see the click event happening, but the index of the item remains stubbornly ‘undefined’.

Hey Chris,

This happens because the click handlers will only be fired at a future time after the loop has completed. After the loop is finished, the value of the variable i is 13, which is not a valid index for the tile array.

What you need to do is ‘capture’ the current value of i when creating each handler, so you’ll have access to it when the handler is fired.

In ES5, you can do this with a closure:

function logTile(tile) {
  return function(e) {
    e.preventDefault();
    console.log('addListener function clicked', tile);
  }
}

function addListener() {
  var tiles = document.getElementsByClassName('tile');
  for (var i = 0; i < tiles.length; i++) {
    tiles[i].addEventListener('click', logTile(tiles[i]));
  }
}

addListener();

The function logTile is called from within the loop, passing the arguments that you want the handler to have access to. logTile() then returns a function which is what gets attached to the event - this creates a closure, where the returned function retains access to the variables in the scope of the parent function, even after the parent function has finished executing.

ES6 has block scoping, which makes for a simpler solution - just declare i using let instead of var:

// ...
for (let i = 0; i < tiles.length; i++) {
  tiles[i].addEventListener('click', function(e) {
    e.preventDefault();
    console.log('addListener function clicked');
    console.log(tiles[i]);
  });
}
1 Like

OK. I’ll give that a go, in the meantime, dumb question - where’s it storing the information? The Codepen is just running against static <li> markup in the HTML, whereas in realty, I have 256 of them being made up dynamically to create a 16x16 grid of ‘tiles’.

I just tried the let instead of var approach, but that gave me the full HTML as an output

<li class="tile">List item index: 6</li>

All I need is the index value as a number, that I can then push into an array.

I think I found an easier approach - when I was generating the <li> elements, I set a data-value attribute on them equal to i. That may well prove a more useful way of accessing things. More testing coming up.

You still have access to i by itself if all you want is the index.

Yeah, that’s all I need

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.