Skip to content Skip to sidebar Skip to footer

Addeventlistener Using For Loop Gives Me An Undefined Result

Why does this piece of code give me a result of undefined? I have tested in the web browser. It shows me that li[i] is undefined. And I don't know why and how. I know about closur

Solution 1:

This is because, i is a variable defined outside your event handler function, which is incremented by each iteration. So when you finish the iterations of the for loop, the value of i is equal tolen, which causes li[i] to be undefined. And if you ask me why it's value is not considered during the iteration, it's because your event handler function is only executed when the event occurs (not when you are setting the event handlers by for loop). So you can make a variable inside the function scope which won't be changed by iteration. Better use this from inside the event handler to get the same thing.

for(var i=0,len=li.length; i<len; i++){
   li[i].addEventListener('click',function(e){
         e.preventDefault();
         alert(this);
         vardata = this.dataset.info;
         showDiv.innerHTML = data;
   },false);
} 

Understanding the for loop

Why the value of i is equal to len after for loop is finished? Let's have a simpler example to make you understand the situation.

varlen = 2;
for(var i=0; i<len; i++; {
    //Do anything here
}
console.log("after for loop: i = " + i);

Lets's go through the iterations.

  1. i = 0, matches the condition i<len, which proceeds with executing the code block and executes i++ after that, which makes i=1;
  2. i = 1 now, matches the condition i<len, which proceeds with executing the code block and executes i++ after that, which makes i=2;
  3. i = 2 now, fails to match the condition i<len and stops the iteration.

So, you have set the i=2 before you go to step 3. So your final console.log after for loop will say, after for loop: i = 2

Solution 2:

This occurs because of scope. Inside the EventListener, you have an anonymous function, because of this, you are no longer in the scope of li. However, you can refer to it with the this keyword. See the alert that I've replaced in your code.

(function(){
  var innerDiv = document.getElementsByClassName('showDiv')[0];
  //var li = document.querySelectorAll('ul.idiv li'); var li = document.getElementsByTagName('li'); 

  for(var i=0,len=li.length; i<len; i++){
       li[i].addEventListener('click',function(e){ // you are now inside a different function block here.
             e.preventDefault();
             alert(this.innerHTML);
             var data = li[i].dataset.info;
             showDiv.innerHTML = data;
       },false);
    
  } 
}()); 
<divclass="showDiv">Show data: </div><divclass="outerDiv"><ulclass="idiv"><lidata-info="datashow"><ahref="">111</a></li><lidata-info="dataHide"><ahref="">222</a></li><lidata-info="dataToggle"><ahref="">333</a></li></ul></div>

Solution 3:

Another approach in addition to the answers of other posters is to use a local scope with function. In JavaScript function is the most reliable way of creating a new scope. Taking the above example,

for (var i = 0; i < li.length; i++) {
    (function(i) {
        var j = i;
        li[j].addEventListener('click', function(e) {
        e.preventDefault();
        var data = li[j].dataset.info;
        showDiv.innerHTML = data;
    }, false);
  })(i);
}

I have created a new scope that that will isolate the value of variable i. If you're using ES6 you can use let keyword to do just the same thing. All you have to do is replace var with let. You can also use let in a for loop that will give you a whole new scope that is local to for loop.

Solution 4:

You need to refer to the particular li on which you want to attach the event

(function(){
      var innerDiv = document.getElementsByClassName('showDiv')[0];
       var li = document.getElementsByTagName('li'); 
          for(var i=0,len=li.length;i<len;i++){
           var thisLi = li[i];
           thisLi.addEventListener('click',function(e){
                 e.preventDefault();
                 alert(thisLi.innerHTML);
                 var data = thisLi.getAttribute('data-info');
                 innerDiv.innerHTML = data;
           },false);
      } 
    }());

When click will be executed i will have value as 3 (in your case) so li[i]will be different. You can check the console of THIS to check value of i on click event

Post a Comment for "Addeventlistener Using For Loop Gives Me An Undefined Result"