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.

  • 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.) – Pointy Sep 19 '12 at 17:43
  • possible duplicate of [Assign click handlers in for loop](http://stackoverflow.com/questions/4091765/assign-click-handlers-in-for-loop) - this is a very common problem as the nature of the behavior involved is not obvious. – Pointy Sep 19 '12 at 17:44
  • Ah. Thanks for pointing out that I forgot to declare the variable inside the loop. –  Sep 19 '12 at 17:47

1 Answers1

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.

Shmiddty
  • 13,847
  • 1
  • 35
  • 52
  • 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. –  Sep 19 '12 at 17:57
  • 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. –  Sep 19 '12 at 18:09
  • @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 Sep 19 '12 at 19:38
  • @Shmiddty I'm pretty sure that the index parameter is required. See the difference between http://jsfiddle.net/v4sSD/ and http://jsfiddle.net/vfwnU/ – Rag Nov 22 '12 at 05:09