AddEventListener using for loop and missing values

I am trying to add an event listener to several objects using a for loop, but in the end all the listeners aim the same object -> the last.

If I add listeners manually, defining boxa and boxb for each instance, it works. I assume addEvent for-loop does not work as I hoped. Perhaps I generally use the wrong approach.

Example of using container 4 class = "A trigger on container 4 works the way it should. A trigger on a container 1,2,3 triggers an event on container 4, but only if the trigger is already activated.

Function to start when pressed:

function makeItHappen(elem, elem2){ var el = document.getElementById(elem); el.style.transform = "flip it"; var el2 = document.getElementById(elem2); el2.style.transform = "flip it"; } 

Startup function to add listeners:

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } } 

HTML code

 <div class="container"> <div class="one" id="box1"> <p class="triggerClass">some text</p> </div> <div class="two" id="box2"> <p class="triggerClass">some text</p> </div> </div> <div class="container"> <div class="one" id="box3"> <p class="triggerClass">some text</p> </div> <div class="two" id="box4"> <p class="triggerClass">some text</p> </div> </div> 
+74
javascript for-loop addeventlistener
Oct 25 '13 at 9:32
source share
5 answers

Shutters !: D

This fixed code works the way you planned:

 for (var i = 0; i < elem.length; i += 2) { (function () { var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function() { makeItHappen(boxa,boxb); }, false); elem[k].addEventListener("click", function() { makeItHappen(boxb,boxa); }, false); }()); // immediate invocation } 



Why is this fix?

 for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } 

JavaScript is actually loose. It is interpreted as follows:

 var i, k, boxa, boxb; for(i=0; i < elem.length; i+=2){ k = i + 1; boxa = elem[i].parentNode.id; boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } 

Due to the lifting variable , var declarations will be moved to the top of the area. Since JavaScript does not have a block area ( for , if , while , etc.), they move to the beginning of the function. Update: Compared to ES6, you can use let to get variables with block spanning.

When you run your code, the following happens: in the for loop, you add callbacks and assign boxa , but its value is overwritten at the next iteration. When the click event fires callbacks and the boxa value boxa always the last item in the list.

Using closure (closing the boxa , boxb , etc.) values, you bind this value to the scope of the click handler.




Code analysis tools like JSLint or JSHint can detect suspicious code like this. If you write a lot of code, it is worth the time to learn how to use these tools. Some IDEs have built-in features.

+115
Oct 25 '13 at 9:34 on
source share

You encounter the scope / close problem as function(){makeItHappen(boxa,boxb);} boxa and boxb links, and then always one of the following elements.

To solve the problem:

 function makeItHappenDelegate(a, b) { return function(){ makeItHappen(a, b) } } // ... elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false); 
+12
Oct. 25 '13 at 9:37 on
source share

You can use the Binding.You function; no need to use closure. See below:

Before

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } } 

After :

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", makeItHappen.bind(this, boxa, boxb), false); elem[k].addEventListener("click", makeItHappen.bind(this, boxa, boxb), false); } } 
+6
Aug 09 '16 at 20:47
source share

I also had this problem a while ago. I solved this using the add function outside the loop to assign events, and it worked perfectly.

Your script should look like this.

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; //- edit ---------------------------| adds(boxa, boxb); } } //- adds function ----| function adds(obj1, obj2){ obj1.addEventListener("click", function(){makeItHappen(obj1, obj2);}, false); obj2.addEventListener("click", function(){makeItHappen(obj1, obj2);}, false); } //- end edit -----------------------| function makeItHappen(elem, elem2){ var el = document.getElementById(elem); el.style.transform = "flip it"; var el2 = document.getElementById(elem2); el2.style.transform = "flip it"; } 
+1
Jan 10 '16 at 1:52
source share

This is due to closure.

Check this out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

The sample code and your code are essentially the same, this is a common mistake for those who don’t know the “closure”.

Simply put, when you create a handler function inside addEvents() , it not only accesses the variable i from the addEvents() environment, but also “remembers” i .

And since your handler “remembers” i , the variable i will not disappear after addEvents() executed.

So, when the handler is called, it will use i , but the variable i now, after the for, 3 loop.

+1
Jun 24 '16 at 15:43
source share



All Articles