views:

227

answers:

5

Hi

I'm trying to pass a parameter in the onclick event. Below is a sample code:

<div id="div"></div>

<script language="javascript" type="text/javascript">
   var div = document.getElementById('div');

   for (var i = 0; i < 10; i++) {
       var link = document.createElement('a');
       link.setAttribute('href', '#');
       link.innerHTML = i + '';
       link.onclick=  function() { onClickLink(i+'');};
       div.appendChild(link);
       div.appendChild(document.createElement('BR'));
       }

   function onClickLink(text) {
       alert('Link ' + text + ' clicked');
       return false;
       }
    </script>

However whenever I click on any of the links the alert always shows 'Link 10 clicked'!

Can anyone tell me what I'm doing wrong?

Thanks

+3  A: 

This happens because the i propagates up the scope once the function is invoked. You can avoid this issue using a closure.

for (var i = 0; i < 10; i++) {
   var link = document.createElement('a');
   link.setAttribute('href', '#');
   link.innerHTML = i + '';
   link.onclick = (function() {
      var current_i = i;
      return function() { 
          onClickLink(current_i+'');
      }
   })();
   div.appendChild(link);
   div.appendChild(document.createElement('BR'));
}

Or if you want more concise syntax, I suggest you use Nick Craver's solution.

Jamie Wong
Thanks, it's working now!
fatcatz
A: 

Try this:

<div id="div"></div>

<script language="javascript" type="text/javascript">
   var div = document.getElementById('div');

   for (var i = 0; i < 10; i++) {
       var f = function() {
           var link = document.createElement('a');
           var j = i; // this j is scoped to our anonymous function
                      // while i is scoped outside the anonymous function,
                      //  getting incremented by the for loop
           link.setAttribute('href', '#');
           link.innerHTML = j + '';
           link.onclick=  function() { onClickLink(j+'');};
           div.appendChild(link);
           div.appendChild(document.createElement('br')); // lower case BR, please!
       }(); // call the function immediately
   }

   function onClickLink(text) {
       alert('Link ' + text + ' clicked');
       return false;
   }
</script>
LeguRi
A: 
link.onclick = function() { onClickLink(i+''); };

Is a closure and stores a reference to the variable i, not the value that i holds when the function is created. One solution would be to wrap the contents of the for loop in a function do this:

for (var i = 0; i < 10; i++) (function(i) {
    var link = document.createElement('a');
    link.setAttribute('href', '#');
    link.innerHTML = i + '';
    link.onclick=  function() { onClickLink(i+'');};
    div.appendChild(link);
    div.appendChild(document.createElement('BR'));
}(i));
Ryan Tenney
I think you've got some brackets misbalanced there. Should it be `})(i);` on the last line?
Jamie Wong
That works, as does omitting the outer parentheses, but this is the "Crockford-approved" style: http://stackoverflow.com/questions/939386/javascript-immediate-function-invocation-syntax
Ryan Tenney
Ah - good to know.
Jamie Wong
+2  A: 

This is happening because they're all referencing the same i variable, which is changing every loop, and left as 10 at the end of the loop. You can resolve it using a closure like this:

link.onclick = function(j) { return function() { onClickLink(j+''); }; }(i);

You can give it a try here

Or, make this be the link you clicked in that handler, like this:

link.onclick = function(j) { return function() { onClickLink.call(this, j); }; }(i);

You can try that version here

Nick Craver
Just to be clear: are you returning the declaration of an anonymous function which accepts a parameter _j_ and at the same moment you are passing the actual parameter too? (the _(i)_)
dierre
@dierre - The `(i)` invokes the `function(j)` with the parameter being the current `i`, triggering it to return the function in which a copied value is in the closure, no longer connected to `i`, make sense?
Nick Craver
It seems the same thing I'm saying but I'm not sure:to me _function(j) { return function() { onClickLink(j+''); }; }_ is _MyAnonymFunction(j)_.What I'm seeing is _link.onclick = MyAnonymFunction(i);_
dierre
@dierre - Use the back tick (tilde key) for comment code :) I'm not sure how to describe it differently, you're invoking the anonymous function that returns another anonymous function, but with a different variable since it came from a different closure (the outer function).
Nick Craver
@Nick Craver: I've just discovered the Ctrl-K shortcut but does not work on comments. BTW I got it. I'm sorry for my english :)
dierre
A: 

or you could use this line:

 link.setAttribute('onClick', 'onClickLink('+i+')');

instead of this one:

link.onclick=  function() { onClickLink(i+'');};
aniri