This question already has an answer here:
-
JavaScript closure inside loops – simple practical example
39 answers
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?
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)
}
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)
}