admin管理员组文章数量:1192355
Ok, here's a problem script.
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.onclick = function() { alert( i ) }
document.body.appendChild( a );
}
This script generates three divs: one, two and three, using an array.
I've set a (Dom0 for simplicity) click handler on each div which alerts the index of its position in the array. - except it doesn't! It always alerts 3, the last index of the array.
This is because the 'i' in 'alert( i )' is a live reference to the outer scope (in this case global) and its value is 3 at the end of the loop.
What it needs is a way of de-referencing i within the loop.
This is one solution and I tend to use it.
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.i = i; //set a property of the current element with the current value of i
a.onclick = function() { alert( this.i ) }
document.body.appendChild( a );
}
Does anyone else do anything different?
Is there a really smart way of doing it?
Does anyone know how the libraries do this?
Ok, here's a problem script.
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.onclick = function() { alert( i ) }
document.body.appendChild( a );
}
This script generates three divs: one, two and three, using an array.
I've set a (Dom0 for simplicity) click handler on each div which alerts the index of its position in the array. - except it doesn't! It always alerts 3, the last index of the array.
This is because the 'i' in 'alert( i )' is a live reference to the outer scope (in this case global) and its value is 3 at the end of the loop.
What it needs is a way of de-referencing i within the loop.
This is one solution and I tend to use it.
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.i = i; //set a property of the current element with the current value of i
a.onclick = function() { alert( this.i ) }
document.body.appendChild( a );
}
Does anyone else do anything different?
Is there a really smart way of doing it?
Does anyone know how the libraries do this?
- Actually, in your first code sample above, the alert displays 3 rather than 2 as i incrementing to 3 causes the loop to stop -- I know it's nit picky, but just thought I'd bring up! Good question, though! – Peter Meyer Commented Jan 14, 2009 at 14:03
4 Answers
Reset to default 20You need to use this little closure trick - create and execute a function that returns your event handler function.
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.onclick = (function(i) { return function() { alert( i ) } })(i);
document.body.appendChild( a );
}
I'd stay with your own solution, but modify it in the following way:
var links = [ 'one', 'two', 'three' ];
function handler() {
alert( this.i );
}
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.i = i; //set a property of the current element with the current value of i
a.onclick = handler;
document.body.appendChild( a );
}
This way, only one function object gets created - otherwise, the function literal will be evaluated on every iteration step!
A solution via closure is even worse performance-wise than your original code.
I recommend Christophs way with one function since it uses less resources.
Below is another way that stores the value on the function (that is possible because a function is an object) and users argument.callee to get a reference to the function inside the function. In this case it doesn't make much sense, but I show the technique since it can be useful in other ways:
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.onclick = function() { alert( arguments.callee.i ) }
a.onclick.i = i;
document.body.appendChild( a );
}
The technique is useful when your function needs to store persistent information between calls. Replace the part above with this:
a.id="div"+i;
a.onclick = function() {
var me = arguments.callee;
me.count=(me.count|0) + 1;
alert( me.i );
}
and you can later retrieve how many times it was called:
for( var i = 0; i < links.length; i++ ){
alert(document.getElementById("div"+i).onclick.count);
}
It can also be used to cache information between calls.
RoBorg's method is definitely the way to go, but I like a slightly different syntax. Both accomplish the same thing of creating a closure that preserves 'i', this syntax is just clearer to me and requires less modification of your existing code:
var links = [ 'one', 'two', 'three' ];
for( var i = 0; i < links.length; i++ ) (function(i) {
var a = document.createElement( 'div' );
a.innerHTML = links[i];
a.onclick = function() { alert( i ) }
document.body.appendChild( a );
})(i);
本文标签: How can one dereference JavaScript variables when enclosing an outer scopeStack Overflow
版权声明:本文标题:How can one de-reference JavaScript variables when enclosing an outer scope - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738453181a2087607.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论