Forum Moderators: open

Message Too Old, No Replies

Problem when generating DHTML

Loop and other values are always the last one used

         

CgiBin

5:12 pm on May 12, 2005 (gmt 0)

10+ Year Member



When trying to use values in onclick and mouseover functions, it doesn't use the value when the node was created, it seems to use whatever the value is the last time it is set.

<html>
<head>
<script language="javascript">
var foo = new Array( 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten');

function doOL() {
var node = document.getElementById("theBody");

for (var x = 0; x < 10; x++) {
var bar = "foo = "+ foo[x];
var myDiv = document.createElement("div");
myDiv.setAttribute("id", 'div_'+ x);
myDiv.onclick = function() { alert('my ID is: '+ this.id +'\nI think x = '+ x +'\n'+ bar); };
var txt = document.createTextNode('This is DIV '+ x);
myDiv.appendChild(txt);

node.appendChild(myDiv);
}
}
</script>
<style>
div {
margin: 10px;
background-color: silver;
cursor: pointer;
}
</style>
</head>
<body id="theBody" onload="doOL()">
</body>
</html>

The displayed value of the div is correct and goes from 0 to 9. However if you click on any of the divs, it always thinks x = 10.

What I usually have to resort to is call a function and pass the id, then use substr to break it apart and then use that value to call whatever function I originally wanted.

Am I doing something wrong that the onclick function isn't remembering the right values?

Bernard Marx

6:28 pm on May 12, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You have stumbled across a Javascript structure, called a closure.

The anonymous functions assigned to the onclicks are inner functions of

doOL
, and the variables,
x
and
bar
, are local variables of
doOL
. doOL's variables are preserved because the inner functions can still be referenced, even after
doOL
has finished executing.

The final value of x is 10, and that is the value of x that is being returned.

1) If you want a value such as x, attach it as a javascript property of the element inside the loop:

myDiv.x = x;

2) It is more efficient to declare the event handler function separately, then assign it. This way only one single function is created. In my code, I have made it an inner function of doOL, so it doesn't affect the global namespace.


function doOL()
{
var node = document.getElementById("theBody");
for (var x = 0; x < 10; x++)
{
var bar = "foo = "+ foo[x];
var myDiv = document.createElement("div");
myDiv.setAttribute("id", 'div_'+ x);
myDiv.x = x;
myDiv.onclick = div_onclick;
myDiv.appendChild( document.createTextNode('This is DIV '+ x) );
node.appendChild(myDiv);
}

function div_onclick()
{
alert('my ID is:'+ this.id+'\n'+ 'I think x = '+ this.x);
}
}

CgiBin

8:16 pm on May 12, 2005 (gmt 0)

10+ Year Member



Thanks, Yeah I knew it seemed to be a scope problem. You explantion clarified it a little more.

My current work-around does something similar to what you suggested.

Thanks again.