无法取消绑定jQuery自定义事件处理程序

时间:2013-05-10 17:29:23

标签: javascript jquery angularjs

我的页面中有一块代表视图的标记,以及与该视图关联的JS控制器函数。 (这些是Angular,但我认为不重要。)控制器代码侦听从应用程序中其他位置触发的自定义事件,并使用某些特定于控制器的逻辑处理该事件。

我的问题是控制器的事件处理程序连接的次数太多了:每次重新激活视图时都会附加它,导致每次触发自定义事件时处理程序都会运行多次。我只希望处理程序每​​个事件运行一次。

我尝试在绑定之前使用.off()取消绑定处理程序;我试过.one()来确保处理程序只运行一次;我在阅读了与$.proxy() here的互动后尝试了.off()

这是我的代码草图:

// the code inside this controller is re-run every time its associated view is activated
function MyViewController() { 

    /* SNIP (lots of other controller code) */

    function myCustomEventHandler() {
        console.log('myCustomEventHandler has run');
        // the code inside this handler requires the controller's scope
    }

    // Three variants of the same misbehaving event attachment logic follow: 

    // first attempt
    $('body').off('myCustomEvent', myCustomEventHandler);
    $('body').on('myCustomEvent', myCustomEventHandler);
    // second attempt
    $('body').one('myCustomEvent', myCustomEventHandler);
    // third attempt
    $('body').off('myCustomEvent', $.proxy(myCustomEventHandler, this));
    $('body').on('myCustomEvent', $.proxy(myCustomEventHandler, this));
    // all of these result in too many event attachments

};

// ...meanwhile, elsewhere in the app, this function is run after a certain user action
function MyEventSender() {
    $('body').trigger('myCustomEvent');
    console.log('myCustomEvent has been triggered');
};

在我的应用程序中点击并切换到麻烦的视图五次,然后执行运行MyEventSender的操作,我的控制台将如下所示:

myCustomEvent has been triggered
myCustomEventHandler has run
myCustomEventHandler has run
myCustomEventHandler has run
myCustomEventHandler has run
myCustomEventHandler has run

如何让它看起来像这样:

myCustomEvent has been triggered
myCustomEventHandler has run

???

3 个答案:

答案 0 :(得分:2)

为您的事件指定命名空间,然后在重新运行控制器时删除具有所述命名空间的所有事件。

jsbin

$('body').off('.controller');
$('body').on('myCustomEvent.controller', myCustomEventHandler);

答案 1 :(得分:2)

您可以收听主控制器中的范围销毁事件

function MyViewController($scope) { 
    function myCustomEventHandler() {
        console.log('myCustomEventHandler has run');
        // the code inside this handler requires the controller's scope
    }

    $('body').on('myCustomEvent', myCustomEventHandler);    

    $scope.$on("$destroy", function(){
        $('body').off('myCustomEvent', myCustomEventHandler);   
        //scope destroyed, no longer in ng view
    });
}

编辑这是一个angularJS解决方案。当您从一个页面移动到另一个页面时,会不断加载ngview。它会在重复调用函数时反复附加事件。您想要做的是在有人离开视图时取消绑定/删除事件。您可以通过挂钩范围$ destroy(带有美元符号)事件来完成此操作。您可以在此处详细了解:$destroy docs

答案 2 :(得分:1)

问题是当多次调用function MyViewController(){}时,会得到一个单独的myCustomEventHandler实例(附加到当前闭包),因此将其传递给$.off不会取消注册上一个处理程序。

KevinB's answer,事件名称空间,是我建议删除特定处理程序而不需要知道安装了哪个处理程序。如果您可以在删除/隐藏元素时取消注册事件,那么就更好了,然后您可以引用要取消注册的函数,而不必担心删除其他代码可能已添加到同一事件命名空间的处理程序。毕竟,事件名称空间只是一个字符串的全局池,容易发生名称冲突。

If you make your function global,它也可以工作(除了它看起来你需要闭包),但我只是用它来解释问题,使用命名空间

function myCustomEventHandler() {
    console.log('myCustomEventHandler has run');
    // the code inside this handler requires the controller's scope
}

function MyViewController() { 

    // first attempt
    $('body').off('myCustomEvent', myCustomEventHandler);
    $('body').on('myCustomEvent', myCustomEventHandler);
    // second attempt
    $('body').one('myCustomEvent', myCustomEventHandler);
    // third attempt
    $('body').off('myCustomEvent', $.proxy(myCustomEventHandler, this));
    $('body').on('myCustomEvent', $.proxy(myCustomEventHandler, this));

}

// ...meanwhile, elsewhere in the app, this function is run after a certain user action
function MyEventSender() {
    $('body').trigger('myCustomEvent');
    console.log('myCustomEvent has been triggered');
}
MyViewController();
MyViewController();
MyEventSender();

以前的想法

其中一个问题是你没有将相同的函数传递给$.on$.off,所以在这种情况下off不会取消注册

不是问题,留下答案以供参考,因为它不完全直观。如果传递相同的函数和上下文,$.proxy似乎返回对同一绑定函数的引用。 http://jsbin.com/adecul/9/edit