omninonsense
omninonsense

Reputation: 6882

addEventHandler() in a loop has unexpected results

I wasn't sure what to look for, so I couldn't find any previous questions on this topic, and Google didn't help much, either.

(function() {
    var element = function(str) {
        return document.getElementById(str);
        },

        parent = document.getElementsByTagName('ul')[0].getElementsByTagName('li'),
        len = parent.length,

        slides = slides || [];

    for (var i=0; i<len; i++) {
        var link = parent[i].getElementsByTagName('a')[0],
            slide = element(parent[i].getElementsByTagName('a')[0].getAttribute('href').substr(1));


        if (addEventListener) {
            link.addEventListener('click', function(event){
                event.preventDefault();
                alert(slide.getAttribute('id'));
            });
        } else if (attachEvent) {

        }
    }

})();

So, in the above code, within the for loop, I attach an event to all the links (five of them) which should alert the ID of the slide they're pointing to (just for debugging purposes), however they all show the ID of the last slide. I also created a jsFiddle for you to see it in... Action.

I'm assuming I am messing up something relatively simple, I just can't figure out what.

Upvotes: 2

Views: 953

Answers (2)

Dark Falcon
Dark Falcon

Reputation: 44181

Variables in javascript are function-scope even when you declare them inside a block. This loop creates a closure around the variable loop, but loop is reassigned on every iteration. By the time the loop is done, all the closures point to the last element in the array.

To fix this, scope the variable differently:

(function() {
    var element = function(str) {
        return document.getElementById(str);
        },

        parent = document.getElementsByTagName('ul')[0].getElementsByTagName('li'),
        len = parent.length;

    for (var i=0; i<len; i++) {
        var link = parent[i].getElementsByTagName('a')[0],
            slide = element(parent[i].getElementsByTagName('a')[0].getAttribute('href').substr(1));


        if (addEventListener) {
            link.addEventListener('click', (function(slide){
                return function(event) {
                    event.preventDefault();
                    alert(slide.getAttribute('id'))
                }
            })(slide));
        } else if (attachEvent) {

        }
    }

})();

Upvotes: 7

Ming-Tang
Ming-Tang

Reputation: 17651

I have encoutered this bug before, it is related to the binding of the slide, which is very difficult to explain. Here is a fix.

link.addEventListener('click',
    (function(x) {
       return function(event){
                event.preventDefault();
                alert(x.getAttribute('id'));
              }
     })(slide));

Upvotes: 3

Related Questions