3

What am doing is attaching event on a class using a loop and index values are being used in the event handler code. Here is my code:

var classElements=document.getElementsByClassName("a");
for(var i=0; i<4; i++)
{
    classElements[i].onClick=function(){
       alert("Clicked button : "+i);
    }
}

Whenever I click any of the buttons, it alerts:

Clicked Button : 4

What could be the problem?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Sourabh
  • 1,757
  • 6
  • 21
  • 43

2 Answers2

4

JavaScript closes over the object and evaluates it later when it is called. At the time it is called, i is 4.

I think you want something like:

var classElements=document.getElementsByClassName("a");
for(var i=0; i<4; i++)
{
    classElements[i].onClick=function(j) { 
       return function(){
          alert("Clicked button : "+j);
       };
    }(i);
}

EDIT: shown with named functions to make the code more clear

var classElements=document.getElementsByClassName("a");
for(var i=0; i<4; i++)
{
    var makeFn = function(j) { 
       return function(){
          alert("Clicked button : "+j);
       };
    };
    classElements[i].onClick = makeFn(i);
}
Lou Franco
  • 87,846
  • 14
  • 132
  • 192
  • am not sure but I think when you pass an argument while attaching event like this, javascript pass an event object and not the variable we want to pas. – Sourabh Apr 30 '12 at 19:10
  • I pass an argument to a function that returns a function. The returned function is the one that is attached. – Lou Franco Apr 30 '12 at 19:14
  • if it were me, I'd wrap `function(j)` with some outer parens. There can be some ambiguity about how/when you can autoexecute a function like that, and outer parens make it explicit. – Matt Apr 30 '12 at 19:14
  • see when you call a function like e.onclick=function(eve){........}, you are actually attaching a function to the onclick event of e right?, I think eve is the event object not the variable, sorry am still not getting your answer. – Sourabh Apr 30 '12 at 19:19
  • @Sourabh: `function(j) {...` is being invoked immediately, and the `return function(){...` is assigned as the handler. It becomes more clear if you use a named function instead of an inline anonymous function. Perhaps Lou will update with that solution. –  Apr 30 '12 at 19:21
  • You beat me by 5 secs and your answer was still more complete :) – vol7ron Apr 30 '12 at 20:14
2

You need a closure in order to capture the changes of i. As Lou stated this is due to post evaluation.

var classElements=document.getElementsByClassName("a");
for(var i=0; i<4; i++)
    classElements[i].onclick = (function(i){
          return function(){ alert("Clicked button : " + i) }; 
       })(i);
vol7ron
  • 40,809
  • 21
  • 119
  • 172