the Future of the Web
  • Home
  • Hire Us
  • Articles
  • About
  • 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 7 Comments | Add a comment
  • Comments

    1. Mark McDonnell at 5:05pm 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 5:12pm 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 8: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 12:54am on February 10, 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 8: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 7: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 9: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

    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
    • Hire Me
    • About Me
    • Email Me
    • RSS Feed RSS Icon
  • Recent Articles

    • Parse Accept-Language to detect a user's language
    • Twitter
    • Three years of The Future of the Web
    • Saving data to a file with PHP
    • Easy web scraping with PHP
    • See all the articles
    • IBM: Where and when to use Ajax
    • Code Igniter 1.6.0 Released
    • Update a Dev Site Automatically with Subversion
    • JavaScript Functions are Variables
    • See All...
  • Categories

    • javascript (37)
    • links (19)
    • about (18)
    • web (14)
    • server (10)
    • html (10)
    • css (8)
    • carnival (7)
    • browsers (7)
    • design (4)
    • seo (4)
    • ads (4)
    • standards (4)
    • events (4)
    • work (4)
  • Older Articles

    • 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 © 2008 Jesse Skinner | CSS | XHTML | RSS | Help | Impressum | Cutie Quilts | Internet Blog Top Sites