绑定到事件时,Javascript闭包的行为会有所不同

sac*_*eie 12 javascript jquery closures jquery-ui

我试图使用闭包来确保函数只能执行一次.听起来很简单,它的工作原理如下:

function runOnce(fn)  // returns copy of fn which can only execute once
{
    var ran = false;

    return function() 
    {
        if (!ran)
        {
            fn();
            ran = true;
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

我已经测试了这个函数:

function lazyLoadGrid(event, ui)
{
    alert('hi');
}

var test1 = runOnce(lazyLoadGrid);
var test2 = runOnce(lazyLoadGrid);

test1();
test2();

test1();
test2();
Run Code Online (Sandbox Code Playgroud)

并且它按预期工作 - 'hi'被提醒两次.

但后来我尝试使用runOnce(lazyLoadGrid)作为jQuery UI事件的回调:

$('.accordion').each(function() 
{ 
    $(this).accordion({ autoHeight: false, change: runOnce(lazyLoadGrid) });
});
Run Code Online (Sandbox Code Playgroud)

疯狂随之而来.我期待的是,当首次打开手风琴时,页面上的每个'手风琴'都会运行一次lazyLoadGrid().相反,闭包回调似乎表现得好像它们都引用了'ran'的相同副本.lazyLoadGrid()在我第一次打开任何手风琴时运行,然后再也不会为任何其他手风琴再次运行.记录'ran'的前置条件值表示每次在第一个之后点击任何手风琴时它都是'true'.

对此有何解释?值得注意的是,我有一个奇怪的页面,嵌套的手风琴,以及多个包含手风琴的jQuery UI标签.更糟糕的是,当我切换标签时,闭合实际上确实在任何给定标签的第一个打开的手风琴上运行.任何建议都非常感谢.

gil*_*ly3 1

问题:

我相信您遇到的麻烦是因为您所认为的“手风琴”实际上是一个“面板”。手风琴由一组中的所有面板组成。听起来您想每个面板运行一次,而不是每个手风琴运行一次。以下演示通过在页面上包含两个手风琴来说明这一概念。请注意,它lazyLoadGrid()运行了两次,每个手风琴运行一次:

http://jsfiddle.net/cTz4F/

解决方案:

相反,您想要做的是创建一个自定义事件并在每个面板上调用该事件。然后,您可以利用 jQuery 的内置.one()方法,该方法会为每个元素调用一次事件处理程序:

$('.accordion').accordion({
    autoHeight: false,
    change: function(e, ui) {
        ui.newHeader.trigger("activated");
    }
});
$('.accordion > h3').one("activated", lazyLoadGrid);
Run Code Online (Sandbox Code Playgroud)

工作演示: http://jsfiddle.net/cTz4F/1/