19

The title may not make sense and nor would a description alone, so here's a sample of the code:

for(var a=0;a<10;a++) {
    var b=document.createElement('b')
    b.onclick=function() {
        alert(a)
    }
    b.innerHTML=a
    document.body.appendChild(b)
}

What's odd is that when I set innerHTML to a, it uses the current value of a. So, this code creates ten <b> elements with the values 0, 1, 2, 3, 4, 5, 6, 7, 8, & 9. This works perfectly. What doesn't, however, is the onclick function. It returns the final value of a (10) when any of these elements is clicked. I've tried setting another variable to a and then using that other variable in the onclick event, but still it returns the final value of a. How would I make it use the value of a when onclick is set?

1
  • It's picky and doesn't solve your problem, but variables in javascript have function scope rather than block scope, so you should really define b outside your loop (and a) Commented May 18, 2011 at 17:25

2 Answers 2

35

Try the following

b.onclick= function(arg) {
    return function() {
        alert(arg);
    }
}(a);

The problem you were hitting is that javascript doesn't use block scope. Instead all variables have a lifetime of their containing function. So there was only ever one a captured in all of the onclick functions and they all see it's final value.

The solution works around this by creating a new function and value per scope and then immediately setting that value to the current value of a.

Here's a version that's a bit expanded and perhaps a bit more readable

var createClickHandler = function(arg) {
  return function() { alert(arg); };
}

for(var a=0;a<10;a++) {
    var b=document.createElement('b')
    b.onclick = createClickHandler(a);
    b.innerHTML=a
    document.body.appendChild(b)
}
Sign up to request clarification or add additional context in comments.

6 Comments

That works, but can you explain how? I've never seen this done before.
@Bobby, I just updated my answer with a longer explanation. Did that clear it up?
The scope bit makes sense to me, but not how returning a function works. It does the job, though.
@Bobby, the reason the function works is because it creates a new scope with a separate lifetime from it's containing function. Now instead of 1 scope / lifetime we have 11 (the original function + the 10 created functions). Each of the created ones has a new lifetime which holds the value of a at the time it was created.
What exactly does (a) at the end of the onclick function do?
|
1

One dirty workaround which I found out is to use .value property of the created object to store the argument which we want to pass to .onclick function. So I can do something like

for(var a=0;a<10;a++) {
    var b=document.createElement('b')
    b.value=a
    b.onclick=function() {
        myfunc(this.value)
    }
    b.innerHTML=a
    document.body.appendChild(b)
}
function myfunc(n){
  console.log(n)
}

I am not sure if .value works with every element, but it does for DIV for example.

EDIT: To pass more values then one you can concatenate them using some character like _ and then split it

b.value=x+"_"+y+"_"+z

function myfunc(str){
 t=str.split("_")
 x=t[0]
 y=t[1]
 z=t[2]
 console.log(x+","+y+","+z)
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.