Do you ever try to define a function in a loop, like an event handler for example, and find that it's always the last variable in the array that is pointed at by every event handler? It can take a while to debug this stuff. Let's say you do something like this:
var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
var link = links.item(i);
link.onclick = function() {
alert(link.href);
return false;
};
}
(Stupid example, I know). Well if you run this on a page, you would expect that everytime you click a link, you will get an alert with the href of that link. Well that's wrong! You actually get the href of the last link on the page every time!
This happens because of closures in JavaScript. You would expect that the link variable has the same value inside the onclick function as it does outside. But in fact, every time the loop comes around, link gets a new value, and actually the link variable in all those other onclick functions changes. So when the loop finishes, every onclick function will alert the href of the last link.
So to avoid this, you can do a few things. You can make a separate function that attaches the event like so:
function attachLinkEvent(link) {
link.onclick = function() {
alert(link.href);
return false;
};
}
var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
var link = links.item(i);
attachLinkEvent(link);
}
This works because inside the attachLinkEvent function, the value of link points at the argument, which is a copy of the link in the loop. This way it doesn't change when the original link changes. Confusing? You bet.
Rather than use this workaround, you can just avoid using the link variable altogether in the onclick function, and replace it with this. Inside an event handler like onclick, this will point at the element that the event handler is attached to. In this case, this points at the link:
var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
var link = links.item(i);
link.onclick = function() {
alert(this.href);
return false;
};
}
Of course, real life examples are a bit more complicated. There are also different workarounds to fix the problem. But typically, one of these two solutions will fix things.
For way more information on closures than you will ever need, read Richard Cornford's essay JavaScript Closures
I found this exact problem last year and it drove me barmy trying to figure out what was going wrong.
In the end I used eval() to resolve the issue.
So for example:
var storeNavigation = document.getElementById('id-Navigation');
var storeLinks = storeNavigation.getElementsByTagName('a');
for(var i=0; i<storeLinks.length; i++)
{
eval("storeLinks[i].onmouseover = function(){ alert(" + i + "); }")
}
Yeah, it gets more tricky when you want to access something like the loop iterator that you can't get to with 'this'.
eval() is a nice solution though.
OMG Thanks so much! I was debugging for hours trying to figure out what was wrong, I didn't know why it kept using the last element in the array!
Nice fix!!
Thanks!
Thanks for this tip! Worked great. Like the first commenter, I too needed access to the loop iterator, but my solution was just to pass it as a second parameter to the attachLinkEvent function.
Thanks alot! spent more than 5 hours trying to find out what is wrong with my loop/onClick function build (always showing last item).
Finally you tip solved my problem..
Another solution to this problem is to use a function
var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
var link = links.item(i);
link.onclick = myfunc(i);
}
function myfunc(i) {
return function() {
alert(i);
return false;
}
}
OMG, thank you for making this post... i've been trying to figure out what was wrong for the past day! Thank you so much
thanks guys this was very helpful. I too had this problem and this fix works perfectly. I used Ken's function idea, which was better for my real world problem :)
Thanks alot, you saved at least some of my hair!!
I know this is an old thread but still i wanna thank Mark McDonnell for his very valuable solution of "eval()"
Thanks for the soln. I am new to jave scripting.
In my code, I parse ajson object and create a link using anchoring <a href="........" onclick="........" >link</a> in a for loop.
How can i get know what link they clicked created in the for loop
I tried several of the solutions on this page and in the comments. The only that I could get to work was the one posted by Ken in comment #6, but I have no idea why. Pass the Tylenol.