2013-05-10 126 views
2

我的页面中有一段代表视图的标记,以及与该视图关联的JS控制器函数。 (这些是Angular,但我不相信这个问题。)控制器代码监听从应用程序中其他地方触发的自定义事件,并使用某些特定于控制器的逻辑处理该事件。无法解除绑定jQuery自定义事件处理程序

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

我已经尝试使用.off()在绑定处理程序之前解除绑定处理程序;我试过.one()以确保处理程序只运行一次;并且在阅读其与.off()here的相互作用之后我试过$.proxy()

这里是我的代码草图:

// 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 

???

+0

你可以让你的控制器不做任何事情,如果它不可见?没有修复你的bug,但它是一种解决问题的不同方式 – 2013-05-10 17:31:56

+0

据我所知,Angular事件周期和jQuery事件周期完全不相关(除了它们都绑定到DOM之外)。您应该在'$(function(){...})'调用中将您的事件附加到控制器外部的标记。 – rossipedia 2013-05-10 17:33:57

+0

适用于我:http://jsbin.com/adecul/1/edit – 2013-05-10 17:34:16

回答

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

+0

我喜欢这个答案,现在我很抱歉我的问题特别提到jQuery。我对jQuery为什么没有按预期工作的好奇心并不完全符合我需要解决的问题,但这个答案很好地解决了我的问题。 – tuff 2013-05-10 20:17:47

+0

这很好,我在回答中提出了这个问题,但是我对角度知之甚少,不了解'$ destroy'事件。没有办法产生冲突(就像你可以使用事件命名空间一样) – 2013-05-10 20:26:10

+0

@tuff尽管我不想成为精英主义者(对不起,如果我是),我会建议我对你选择的那个回答。原因是,如果你使用顶级的,并且有人进入了视图,然后你会让这个事件处理程序仍然绑定在某个地方。即使他们再也不会去看。在我的回答中,它会被永久删除。 – 2013-05-13 17:22:21

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

+1

这就是我所期望的(我期望看到它执行了3次),而是我只看到它一次,见我的其他评论jsbin。他甚至得到超过三个暗示其他事情正在发生的事情之外 – 2013-05-10 17:38:23

+0

@KevinB我运行了你的jsbin,但我不确定在这种情况下重新加载视图,我调用了MyEventSender();' – 2013-05-10 17:40:38

+0

是的,没有我用给定的代码做的结果在OP看到。 – 2013-05-10 17:41:13

2

给你的事件一个命名空间,然后当你重新运行控制器时,简单地删除所有带有上述命名空间的事件。

jsbin

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

这可以更容易,然后找出哪些代理处理程序注销 – 2013-05-10 17:49:17

+0

请参阅我的答案解释为什么OP的代码不注销 – 2013-05-10 18:16:15

+0

这不起作用:http://jsbin.com/adecul/17/edit :(我仍然看到“myCustomEventHandler已经运行”x7 – tuff 2013-05-10 20:02:57

相关问题