the Future of the Web
  • Home
  • Articles
  • Contact
  • Defining functions in a loop

    Oct 19 2006

    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

    Tags: javascript tips tricks loop bugs
    View 13 Comments | Add a comment
  • Comments

    1. Mark McDonnell at 11:05am on October 20, 2006

    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 + "); }")

    }

    2. Jesse Skinner at 11:12am on October 20, 2006

    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.

    3. Thomas Chan at 2:13am on January 5, 2007

    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!

    4. Andy at 6:54pm on February 9, 2007

    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.

    5. Andy at 2:40pm on April 11, 2007

    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..

    6. ken at 1:46pm on June 5, 2007

    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;
        }
    }

    7. Frank at 3:22pm on September 6, 2007

    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

    8. tim at 2:53pm on January 15, 2009

    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 :)

    9. Simon Harper at 9:01am on May 15, 2009

    Thanks alot, you saved at least some of my hair!!

    10. Gaurav Singh at 10:11am on July 11, 2009

    I know this is an old thread but still i wanna thank Mark McDonnell for his very valuable solution of "eval()"

    11. Ravi at 12:57pm on March 2, 2010

    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

    12. Ravi at 1:02pm on March 2, 2010

    I guess i got it. thanks a ton

    13. Dan Gadd at 4:45pm on March 9, 2010

    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.

    Add a Comment

    Note: HTML tags and entities will be converted so that they are displayed as you type them. This means if you type in <em>, people will see <em>, and if you type &lt;em&gt;, people will see &lt;em&gt;.

  • Request a Quote

  • Jesse Skinner

    Jesse Skinner
    • About Me
    • Email Me
    • RSS Feed RSS Icon
    • @JesseSkinner
  • Recent Articles

    • jQuery Live Events
    • I need web developers
    • buttons need type="submit" to submit in IE
    • Win $200 in a Web Dev Writing Contest
    • Use Arrays in HTML Form Variables
    • 5 Reasons Freelancers Can Succeed in a Shrinking Economy
    • Keeping a Live Eye on Logs
    • Using PHP's empty() Instead of isset() and count()
    • Testing Web Pages with Lynx
    • Stop CSS Background Flickering in Internet Explorer 6
    • See All...
  • Categories

    • javascript (39)
    • links (21)
    • about (19)
    • web (14)
    • html (12)
    • server (11)
    • css (8)
    • browsers (8)
    • carnival (7)
    • work (6)
    • design (4)
    • seo (4)
    • ads (4)
    • standards (4)
    • events (4)
  • Older Articles

    • February 2009
    • January 2009
    • December 2008
    • November 2008
    • October 2008
    • July 2008
    • June 2008
    • May 2008
    • April 2008
    • February 2008
    • January 2008
    • December 2007
    • November 2007
    • September 2007
    • August 2007
    • July 2007
    • June 2007
    • May 2007
    • April 2007
    • March 2007
    • February 2007
    • January 2007
    • December 2006
    • November 2006
    • October 2006
    • September 2006
    • August 2006
    • July 2006
    • June 2006
    • May 2006
    • April 2006
    • March 2006
    • February 2006
    • January 2006
    • December 2005
    • November 2005
    • October 2005
    • September 2005
    • August 2005
    • April 2005
    • See All...
Copyright © 2010 The Future of the Web