Sunday, February 12, 2012

Javascript closure with iteration

This is something with Javascript that tripped me up recently:

I had a geographical US map, with the different states highlighted - I used a jquery plugin called maphilight to get the highlights working, and needed to respond to the click event on the southern US states, I had a code along these lines:
var south=["VA", "WV", "KY", "TN", "NC", "SC", "GA", "AL", "MS", "FL", "LA", "AR"];
            for (var i=0;i<south.length;i++){
                $("[title=" + south[i] + "]").click(function(){
                    alert(south[i] + " clicked");
                });
            }


This however does not work - if I click on any of the southern states, the result is actually "undefined clicked".

The reason for this behavior is the closure defined as the click handler - it has a reference to variable "i" and NOT it's value at the point of defining the function, and is resolved only at the point of calling it, which is as a response to the click event. Since it is defined within a loop, i's value at the end of the loop is 12 at the end of the loop, and is resolved as south[12] in the function, and a southern state at this index does not exist

The fix is interesting, I got it from the book - Secrets of the Javascript Ninja and is to use something called an immediate function which looks something like this:
(function(){
.....
})()

Using the immediate function, the fixed code looks like this:
var south=["VA", "WV", "KY", "TN", "NC", "SC", "GA", "AL", "MS", "FL", "LA", "AR"];
            for (var i=0;i<south.length;i++){
                (function(index){
                    $("[title=" + south[index] + "]").click(function(){
                        alert(south[index] + " clicked");
                    });
                 })(i);            

No comments:

Post a Comment