3

I have a list of objects each of which has a .bullet which is a SPAN. I want to bind a click on the span to a handler than performs a certain action on the span using jQuery. I'm seeing some behavior I don't understand, so I'm hoping someone can explain what's going on. Basically, this first code example works:

for (var i = 0 ; i< length ; i++) {

            (function(){
                dataNode = dataNodeList[i];

                var handler = function(e) {

                    e.data.node.bullet.firstChild.nodeValue = "- ";


                };


                $(dataNode.bullet).on("click",{node:dataNode},handler);


            })();

        }

However, this second variation does not work:

for (var i = 0 ; i< length ; i++) {

            (function(){
                dataNode = dataNodeList[i];

                var handler = function() {

                    dataNode.bullet.firstChild.nodeValue = "- ";


                };


                $(dataNode.bullet).on("click",handler);


            })();

        }

In this second example,

dataNode.bullet.firstChild.nodeValue = "- ";

has no effect on the value of the SPAN I intended. I expected dataNode.bullet to still point to the SPAN I want to change because of JavaScript closure. So, can someone explain why this fails? Thanks.

3
  • Two things: declare "dataNode" in that function with var, and pass "i" to that immediately-invoked function in the loop. (Add "i" to its parameter list too of course.) Commented Sep 19, 2012 at 17:43
  • possible duplicate of Assign click handlers in for loop - this is a very common problem as the nature of the behavior involved is not obvious. Commented Sep 19, 2012 at 17:44
  • Ah. Thanks for pointing out that I forgot to declare the variable inside the loop. Commented Sep 19, 2012 at 17:47

1 Answer 1

6

Try this:

for (var i = 0 ; i< length ; i++) {
    (function(index){
        var dataNode = dataNodeList[index];

        var handler = function() {
            dataNode.bullet.firstChild.nodeValue = "- ";
        };

        $(dataNode.bullet).on("click",handler);
    })(i);
}

The closure defines a new scope. This is necessary because your handler isn't called until after the loop has finished, so i is not part of the scope at the time it is called, or (in some cases) has the last possible value from the loop.

Sign up to request clarification or add additional context in comments.

4 Comments

This seems to have solved the main problem. Now, at least dataNode.bullet seems to be pointing to the right span element. Thanks for that. I'm seeing another problem though. dataNode.bullet.firstChild.nodeValue = "- " mysteriously just deletes the contents of the span. However, if I immediately follow that with a line to style the span, then both changes appear. I'm not sure if this is related to the original problem or not.
Could you explain why it was necessary to put the index parameter in the anonymous function? I don't understand conceptually why it would make a difference and, with the important difference of delcaring dataNode inside the function, my code works without that index parameter.
@AdamCarter supplying the index as a parameter to the closure may not be strictly necessary in this case, but it certainly helps to make the code more clear.
@Shmiddty I'm pretty sure that the index parameter is required. See the difference between jsfiddle.net/v4sSD and jsfiddle.net/vfwnU

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.