如果立即反弹,事件处理程序不会解除绑定
Event handlers not unbound if immediately rebound
我有一个呈现按钮的简单指令。
指令的 link 函数执行以下操作:
- 绑定 'mouseenter' 和 'mouseleave' 事件。
- 取消绑定 'mouseenter' 和 'mouseleave' 事件。
- 再次绑定 'mouseenter' 和 'mouseleave' 事件。
事件处理程序将一条简单消息记录到控制台。我希望处理程序在 mouseenter 或 mouseleave 上被调用一次。然而,它们被执行了两次,就好像步骤 2 从未发生过一样。
指令代码:
function ButtonDirective() {
return {
restrict: 'E',
template: '<button><span ng-transclude></span></button>',
transclude: true,
replace: true,
link: function (scope, element, attrs) {
function mouseEnterHandler() {
console.log('mouse enter');
}
function mouseLeaveHandler() {
console.log('mouse leave');
}
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
element.unbind('mouseenter');
element.unbind('mouseleave');
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
}
}
}
以下 plunker 说明了这个问题:
http://plnkr.co/ocXYYZ2jv09Ch7GDRaat
有人知道它为什么会这样吗?
update:如果您包含 jQuery 而不是回退到 JQLite,它就可以工作。唉,这对我来说不是一个选择。
解绑时,必须指定要解绑的功能:
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
element.unbind('mouseenter', mouseEnterHandler);
element.unbind('mouseleave', mouseLeaveHandler);
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
该行为是因为,根据 mouse enter
每次鼠标进入目标元素或其子元素时,都会触发 mouseenter
事件。
应该是mouseover
和mouseout
事件而不是mouseleave
和mouse enter
element.bind('mouseover', mouseEnterHandler);
element.bind('mouseout', mouseLeaveHandler);
element.unbind('mouseover');
element.unbind('mouseout');
element.bind('mouseover', mouseEnterHandler);
element.bind('mouseout', mouseLeaveHandler);
好吧,问题似乎出在 JQLite 中。如果你包含 jQuery 它会按预期工作。
在 JQLite 中,bind() 和 unbind() 函数只是 JQLite.off() 和 on() 的别名。
让我们考虑一下 on() 函数的代码。
on: function jqLiteOn(element, type, fn, unsupported) {
// ...
}
此函数为各种类型的事件注册处理程序。显然他们为 'mouseenter' 和 'mouseleave' 事件破例。
if (type === 'mouseenter' || type === 'mouseleave') {
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
var target = this, related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if (!related || (related !== target && !target.contains(related))) {
handle(event, type);
}
});
}
这些事件分别映射(MOUSE_EVENT_MAP[类型])到'mouseover'和'mouseout'。
var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
然后 jqLiteOn(...) 函数递归调用自身并为这些映射事件注册一个匿名函数处理程序。之后它会注册您的 'mouseenter' 和 'mouseleave' 事件。
所以基本上这就是事件处理程序被多次调用的原因。
解决方法是同时取消绑定 mouseover 和 mouseout 事件。
element.unbind('mouseover mouseenter');
element.unbind('mouseout mouseleave');
我有一个呈现按钮的简单指令。
指令的 link 函数执行以下操作:
- 绑定 'mouseenter' 和 'mouseleave' 事件。
- 取消绑定 'mouseenter' 和 'mouseleave' 事件。
- 再次绑定 'mouseenter' 和 'mouseleave' 事件。
事件处理程序将一条简单消息记录到控制台。我希望处理程序在 mouseenter 或 mouseleave 上被调用一次。然而,它们被执行了两次,就好像步骤 2 从未发生过一样。
指令代码:
function ButtonDirective() {
return {
restrict: 'E',
template: '<button><span ng-transclude></span></button>',
transclude: true,
replace: true,
link: function (scope, element, attrs) {
function mouseEnterHandler() {
console.log('mouse enter');
}
function mouseLeaveHandler() {
console.log('mouse leave');
}
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
element.unbind('mouseenter');
element.unbind('mouseleave');
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
}
}
}
以下 plunker 说明了这个问题:
http://plnkr.co/ocXYYZ2jv09Ch7GDRaat
有人知道它为什么会这样吗?
update:如果您包含 jQuery 而不是回退到 JQLite,它就可以工作。唉,这对我来说不是一个选择。
解绑时,必须指定要解绑的功能:
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
element.unbind('mouseenter', mouseEnterHandler);
element.unbind('mouseleave', mouseLeaveHandler);
element.bind('mouseenter', mouseEnterHandler);
element.bind('mouseleave', mouseLeaveHandler);
该行为是因为,根据 mouse enter
每次鼠标进入目标元素或其子元素时,都会触发 mouseenter
事件。
应该是mouseover
和mouseout
事件而不是mouseleave
和mouse enter
element.bind('mouseover', mouseEnterHandler);
element.bind('mouseout', mouseLeaveHandler);
element.unbind('mouseover');
element.unbind('mouseout');
element.bind('mouseover', mouseEnterHandler);
element.bind('mouseout', mouseLeaveHandler);
好吧,问题似乎出在 JQLite 中。如果你包含 jQuery 它会按预期工作。
在 JQLite 中,bind() 和 unbind() 函数只是 JQLite.off() 和 on() 的别名。
让我们考虑一下 on() 函数的代码。
on: function jqLiteOn(element, type, fn, unsupported) {
// ...
}
此函数为各种类型的事件注册处理程序。显然他们为 'mouseenter' 和 'mouseleave' 事件破例。
if (type === 'mouseenter' || type === 'mouseleave') {
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
var target = this, related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if (!related || (related !== target && !target.contains(related))) {
handle(event, type);
}
});
}
这些事件分别映射(MOUSE_EVENT_MAP[类型])到'mouseover'和'mouseout'。
var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
然后 jqLiteOn(...) 函数递归调用自身并为这些映射事件注册一个匿名函数处理程序。之后它会注册您的 'mouseenter' 和 'mouseleave' 事件。
所以基本上这就是事件处理程序被多次调用的原因。
解决方法是同时取消绑定 mouseover 和 mouseout 事件。
element.unbind('mouseover mouseenter');
element.unbind('mouseout mouseleave');