【问题标题】:Event handlers not unbound if immediately rebound如果立即反弹,事件处理程序不会解除绑定
【发布时间】:2015-04-21 12:34:52
【问题描述】:

我有一个简单的指令来呈现一个按钮。

指令的链接函数做了以下工作:

  1. 绑定“mouseenter”和“mouseleave”事件。
  2. 取消绑定“mouseenter”和“mouseleave”事件。
  3. 再次绑定“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

有人知道它为什么会这样吗?

更新:如果你包含 jQuery 而不是依赖 JQLite,它就可以工作。唉,这不是我的选择。

【问题讨论】:

    标签: angularjs jqlite


    【解决方案1】:

    嗯,问题似乎出在 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[type])到“mouseover”和“mouseout”。

    var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
    

    然后 jqLit​​eOn(...) 函数递归调用自身并为这些映射事件注册一个匿名函数处理程序。之后它会注册您的“mouseenter”和“mouseleave”事件。

    所以基本上这就是事件处理程序被多次调用的原因。

    解决方法是同时取消绑定 mouseover 和 mouseout 事件。

    element.unbind('mouseover mouseenter');
    element.unbind('mouseout mouseleave');
    

    【讨论】:

      【解决方案2】:

      解绑时,必须指定要解绑的函数:

        element.bind('mouseenter', mouseEnterHandler);
        element.bind('mouseleave', mouseLeaveHandler);
      
        element.unbind('mouseenter', mouseEnterHandler);
        element.unbind('mouseleave', mouseLeaveHandler);
      
        element.bind('mouseenter', mouseEnterHandler);
        element.bind('mouseleave', mouseLeaveHandler);
      

      【讨论】:

      • 是的,没错,但这是可选的。还尝试使用该功能。结果相同。
      • 是的,你是对的,有趣的是,你可以继续复制 unbind/bind,数量也会增加。
      【解决方案3】:

      这种行为是因为,根据mouse enter,每次鼠标进入目标元素或其子元素时,都会触发mouseenter 事件。

      应该是mouseovermouseout 事件而不是mouseleavemouse enter

        element.bind('mouseover', mouseEnterHandler);
        element.bind('mouseout', mouseLeaveHandler);
      
        element.unbind('mouseover');
        element.unbind('mouseout');
      
        element.bind('mouseover', mouseEnterHandler);
        element.bind('mouseout', mouseLeaveHandler);
      

      【讨论】:

      • 不幸的是,这不是这里的问题。我更新了 plunker 并从指令中删除了嵌入。现在只是按钮,删除了跨度。结果相同。
      • 您是否尝试过 mouseover 和 mouseout 事件?
      • 这不是这里的问题。它应该取消绑定事件。我知道它会为任何孩子冒泡事件,但在这种情况下没有。正如 Mathew Berg 在他的评论中提到的,您可以根据需要复制绑定/取消绑定序列,并且数量会增加。只是奇怪。
      猜你喜欢
      • 1970-01-01
      • 2022-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-10
      • 1970-01-01
      • 2012-08-05
      • 1970-01-01
      相关资源
      最近更新 更多