I am trying to use a closure to ensure that a function can only execute once. Sounds simple, and it works like this:
function runOnce(fn) // returns copy of fn which can only execute once
{
var ran = false;
return function()
{
if (!ran)
{
fn();
ran = true;
}
};
}
I have tested the function like so:
function lazyLoadGrid(event, ui)
{
alert('hi');
}
var test1 = runOnce(lazyLoadGrid);
var test2 = runOnce(lazyLoadGrid);
test1();
test2();
test1();
test2();
And it works as expected – ‘hi’ gets alerted exactly twice.
But then I try to use runOnce(lazyLoadGrid) as the callback to a jQuery UI event:
$('.accordion').each(function()
{
$(this).accordion({ autoHeight: false, change: runOnce(lazyLoadGrid) });
});
And madness ensues. What I expect is that each ‘accordion’ on the page will run lazyLoadGrid() exactly once, when that accordion is first opened. Instead, the closure callbacks seem to behave as if they are all referencing the same copy of ‘ran’. lazyLoadGrid() runs the first time I open any accordion, and then never runs again for any other accordion. Logging the pre-condition value of ‘ran’ shows that it’s ‘true’ every time I click any accordion after the first one.
What is the explanation for this? It may be worth noting I have an odd page, with nested accordions, and multiple jQuery UI tabs each containing accordions. To make matters worse, when I switch tabs the closure actually does run on the first-opened accordion of any given tab. Any advice is much appreciated.
The problem:
I believe the trouble you are having is because what you are thinking of as an “accordion” is actually a “panel”. The accordion consists of all the panels in a group. It sounds like you want to run it once per panel, not once per accordion. The following demo illustrates the concept by including two accordions on a page. Notice that
lazyLoadGrid()is run twice, once for each accordion:http://jsfiddle.net/cTz4F/
The solution:
Instead what you want to do is create a custom event and call that event on each panel. Then you can take advantage of jQuery’s built-in
.one()method which causes that an event handler is called exactly once for each element:Working demo: http://jsfiddle.net/cTz4F/1/