Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing parameters on event listeners with loops

Just a quick question, can anyone tell me why this doesnt work and how to fix it? Essentially its taking a group of table rows in HTML and dynamically attaching click events to them.

for (var a=index; a<rows.length; a++) {
    tr = rows[a];
    tr.onclick = function() { DropDownManager.onItemClick(tr, a); };
    tr.observe("click", function() { DropDownManager.onItemClick(tr, a); });
}

The problem with this code is that the values passed into DropDownManager.onItemClick are always the last items in the loop, this isn't what im after as i wanted them to be the current value in that stage of the loop. I realize I'm missing something quite simple but cant for the life of me work it out!

like image 454
Ian Harrigan Avatar asked Aug 06 '11 16:08

Ian Harrigan


2 Answers

JavaScript has no block scope, so e.g. loops don't create a new scope. You can create one by using a function:

for (var a=index; a<rows.length; a++) {
   (function(a) {
      tr = rows[a];
      tr.onclick = function() { DropDownManager.onItemClick(this, a); };
      tr.observe("click", function() { DropDownManager.onItemClick(this, a); });
   }(a));
}

Depending on what rows and tr are, you might not even need the loop index. Maybe you can get the elements index inside the event handler through another way. E.g. if tr is a HTMLTableRowElement [MDN], then you can get its position among the other rows via this.rowIndex.

Btw. why are you binding the same event handler twice?

like image 153
Felix Kling Avatar answered Oct 12 '22 02:10

Felix Kling


Aside from storing the attributes on the DOM object in the loop, you can also use function closures to "freeze" a copy of the variables in the loop for a particular function call. You can do it like this:

for (var a=index; a<rows.length; a++) {
    tr = rows[a];
    tr.onclick = function(tr, a) {
        return(function() { 
            DropDownManager.onItemClick(tr, a); 
        });
    }(tr,a);
}

What this does is says to assign tr.onclick the results of executing an anonymous function call that takes two variables as parameters (named tr and a) and is passed the current values of tr and a as parameters (this passing of the current values "freezes" the current values of those variables inside this function closure.

The result of executing that function call is itself another anonymous function. Because this internal anonymous function is now assigned to tr.onclick, it creates a function closure that keeps alive all the state that is currently in that closure. That state includes the "frozen" values of tr and a, but they are kept internal to just this function closure so every time through the loop creates a new function closure with a new "frozen" and "stored" value of tr and a.

like image 29
jfriend00 Avatar answered Oct 12 '22 01:10

jfriend00