The Objective
I want to dynamically assign event handlers to some divs on pages throughout a site.
My Method
Im using jQuery to bind anonymous functions as handlers for selected div events.
The Problem
The code iterates an array of div names and associated urls. The div name is used to set the binding target i.e. attach this event handler to this div event.
While the event handlers are successfully bound to each of the div events, the actions triggered by those event handlers only ever target the last item in the array.
So the idea is that if the user mouses over a given div, it should run a slide-out animation for that div. But instead, mousing over div1 (rangeTabAll) triggers a slide-out animation for div4 (rangeTabThm). The same is true for divs 2, 3, etc. The order is unimportant. Change the array elements around and events will always target the last element in the array, div4.
My Code - (Uses jQuery)
var curTab, curDiv; var inlineRangeNavUrls=[['rangeTabAll','range_all.html'],['rangeTabRem','range_remedial.html'], ['rangeTabGym','range_gym.html'],['rangeTabThm','range_thermal.html']]; for (var i=0;i<inlineRangeNavUrls.length;i++) { curTab=(inlineRangeNavUrls[i][0]).toString(); curDiv='#' + curTab; if ($(curDiv).length) { $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} ); $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} ); } }
My Theory
I'm either not seeing a blindingly obvious syntax error or its a pass by reference problem. Initially i had the following statement to set the value of curTab:
curTab=inlineRangeNavUrls[i][0];
So when the problem occured i figured that as i changed (via for loop iteration) the reference to curTab, i was in fact changing the reference for all previous anonymous function event handlers to the new curTab value as well.... which is why event handlers always targeted the last div.
So what i really needed to do was pass the curTab value to the anonymous function event handlers not the curTab object reference.
So i thought:
curTab=(inlineRangeNavUrls[i][0]).toString();
would fix the problem, but it doesn't. Same deal. So clearly im missing some key, and probably very basic, knowledge regarding the problem. Thanks.
The syntax is simple: you can simply declare the anonymous function and make it execute by just calling it using the parenthesis at the end of the function. You can simply pass the parameters inside the immediate execution of the anonymous function as we have seen in the above example.
We can write an anonymous function using the function keyword, followed by parentheses (). We can write the function statements, just like we do for any other javascript function inside the curly parentheses {}. We store the results returned by an anonymous function in variables.
The () makes the anonymous function an expression that returns a function object. An anonymous function is not accessible after its initial creation. Therefore, you often need to assign it to a variable. In this example, the anonymous function has no name between the function keyword and parentheses () .
They're called anonymous functions because they aren't given a name in the same way as normal functions. Because functions are first-class objects, we can pass a function as an argument in another function and later execute that passed-in function or even return it to be executed later.
You need to create a new variable on each pass through the loop, so that it'll get captured in the closures you're creating for the event handlers.
However, merely moving the variable declaration into the loop won't accomplish this, because JavaScript doesn't introduce a new scope for arbitrary blocks.
One easy way to force the introduction of a new scope is to use another anonymous function:
for (var i=0;i<inlineRangeNavUrls.length;i++) { curDiv='#' + inlineRangeNavUrls[i][1]; if ($(curDiv).length) { (function(curTab) { $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} ); $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} ); })(inlineRangeNavUrls[i][0]); // pass as argument to anonymous function - this will introduce a new scope } }
As Jason suggests, you can actually clean this up quite a bit using jQuery's built-in hover()
function:
for (var i=0;i<inlineRangeNavUrls.length;i++) { (function(curTab) // introduce a new scope { $('#' + inlineRangeNavUrls[i][1]) .hover( function(){showHideRangeSlidingTabs(curTab, true);}, function(){showHideRangeSlidingTabs(curTab, false);} ); // establish per-loop variable by passsing as argument to anonymous function })(inlineRangeNavUrls[i][0]); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With