简体   繁体   中英

Javascript - undefined array element when adding eventlistener

I am fairly new to javascript and I am trying to grab all the "td" elements in the DOM and add a click event to them. When the code finishes executing, i can see I have 37 elements in cells array, but when I click the element, I get "clicked td37 undefined" in my console statement no matter which element is clicked. I do not have a clue what is wrong and any help would be appreciated.

<script>
window.addEventListener("DOMContentLoaded", createEventListeners, false);

function createEventListeners() {
   var cells = document.getElementsByTagName("td");
   console.log(cells);

   for (var i = 0; i < cells.length; i++) {
      cells[i].addEventListener("click", function () {
      console.log("clicked td" +  i + " " + cells[i]);

      }, false);
   }

 }
</script>

At the end of the loop, the value of i will be the cells.length , so cells[i] is undefined. You need to wrap them in a function so that i is the value you want. Read more about closure here

function createEventListeners() {
  var cells = document.getElementsByTagName("td");

  for (var i = 0; i < cells.length; i++) {
    (function(i) {
      cells[i].addEventListener("click", function () {
        console.log("clicked td" +  i + " " + cells[i]);
      }, false);
    }(i));
}

This is a scope issue. You need a closure. By the time any Event in your loop is called, i looks for it's last know value, which happens to be it's value at the end of the loop. Solution:

var pre = onload; // previous onload
onload = function(){
  if(pre)pre();
  function createEventListeners(){
    var cells = document.getElementsByTagName('td');
    for(var i=0,l=cells.length; i<l; i++){
      (function(i){
        cells[i].addEventListener('click', function(){
          console.log('i is at position:'+i));
        }, false);
      })(i);
    }
  }
  window.addEventListener('DOMContentLoaded', createEventListeners, false);
}

At the end of your loop, i is always the length of the array for anything you click on and since they're zero indexed, no element with that index exists. For example, if there are 10 elements, they're indexed as 0-9, but when you click any of them, i is always 10.

This question+answers are a great example of when and how to use closures. Since this question is tagged with jQuery , the each function would be a way to do it in jQuery . When you use each , it conveniently closes over what you are eaching through, so this would also work:

$(document).ready(function () {
    var $tds = $('td');
    $tds.each(function (i, td) {
        $(td).click(function () {
            console.log(i, td);
        });
    });
});

And also, there is a way to do it without closures. If you store the value of the iterator onto the element itself via a data attribute, it stores a copy of the value, rather than a reference, so it retains the value of i at the time it was assigned:

window.addEventListener("DOMContentLoaded", createEventListeners, false);
function createEventListeners() {
    var cells = document.getElementsByTagName("td");
    console.log(cells);

    for (var i = 0; i < cells.length; i++) {
        cells[i].setAttribute('data-i', i);
        cells[i].addEventListener("click", function () {
            var i = this.getAttribute('data-i');
            console.log("clicked td" + i + " " + cells[i]);

        }, false);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM