【问题标题】:Event listener that is added via addEventListener is being called multiple times通过 addEventListener 添加的事件监听器被多次调用
【发布时间】:2018-12-03 11:55:43
【问题描述】:

我正在使用 addEventListener 对某些事件做出反应。

根据我的阅读,可以在同一个EventTarget上注册多个相同的EventListener,而EventListener只会被调用一次。

这是来自 MDN 的引述:

如果多个相同的 EventListener 在同一个 EventTarget 上注册了相同的参数,则重复的实例将被丢弃。它们不会导致 EventListener 被调用两次,也不需要使用 removeEventListener() 方法手动删除。但是请注意,当使用匿名函数作为处理程序时,此类侦听器将不相同,因为即使使用相同不变的源代码定义,匿名函数也不相同,只是重复调用,即使在循环中也是如此。但是,在这种情况下重复定义相同的命名函数可能会带来更多问题。 (请参阅下面的内存问题。)

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Multiple_identical_event_listeners

我不确定为什么在这个例子中没有发生这种情况?运行此代码将调用 EventListener 两次:(Codepen here)

function _callback() { console.log("fnFunction"); }

function init() {
  attachGesture("panel-anchor", _callback);
  attachGesture("panel-anchor", _callback);
}

function attachGesture(sElementId, fnFunction) {
  var oElement = document.getElementById(sElementId);

  function foo() {
    console.log("Foo");
    fnFunction();
  }

  oElement.addEventListener("click", foo, false);
}

window.onload = init;

如何确保 EventListener 只被调用一次?

【问题讨论】:

标签: javascript


【解决方案1】:

可以在同一个EventTarget上注册多个相同的EventListener,EventListener只会被调用一次。

不是。

如果你多次注册 same 事件处理器,那么它只会被调用一次。

相同但不是=== 事件处理程序不是相同 事件处理程序。

您需要更改逻辑以避免尝试注册相同的处理程序。

【讨论】:

  • @Shishigami — 不。每次调用attachGesture 时,都会生成一个新版本的foo
  • 避免它们被多次注册会很复杂。有没有办法确保它们是同一个事件处理程序?
  • 在函数外定义foo,这样你就不会每次都创建一个新的。
  • 如果我在外部定义了 foo 中的 fnFunction,如何访问它?
【解决方案2】:

正如 Quentin 已经提到并在 MDN 文档中所写,事件侦听器函数必须相同才能完成您想要实现的目标。

(强调我的)

如果多个相同的EventListener注册在同一个EventTarget上,参数相同,重复的实例将被丢弃。

我尝试使用Function.prototype.bind 来实现这一点,但它会返回原始函数的副本,因此不起作用。

我认为你不能不自己做家务。

"use strict";
console.clear();

function _callback() { console.log("foo fnFunction"); }
function _callback2() { console.log("bar fnFunction"); }

function init() {
  var btn = document.getElementById('btn');
  attachGesture(btn, 'click', _callback);
  attachGesture(btn, 'click', _callback);
  attachGesture(btn, 'click', _callback);
  attachGesture(btn, 'click', _callback2);
  attachGesture(btn, 'click', _callback);
  attachGesture(btn, 'click', _callback);
  attachGesture(btn, 'click', _callback2);
  attachGesture(btn, 'click', _callback);
  attachGesture(btn, 'click', _callback);
}


;(function() {
  var houseKeeping = new Map();
  
  function _attachGesture(element, eventName, callback) {
    if (!houseKeeping.has([element, eventName, callback])) {
      houseKeeping.set([element, eventName, callback], true);
      element.addEventListener(eventName, callback);
    }
  }
  
  window.attachGesture = _attachGesture
})();



window.onload = init;
<button id="btn">Click</button>

【讨论】:

  • 谢谢,这对我的目的应该很有效:)
【解决方案3】:

根据我读到的内容,由于您每次调用它时都在 attachGesture 内重新创建 foo,因此上述函数 foo 每次都与自身的不同实例有关。因此,它们不一样(这违反了您在MDN reference 中提到的内容)。在某处存储对foo 的引用并分配该引用应该可以解决问题。

简单来说,你可以试试这样的

let _callback = ()=>{ console.log("fnFunction"); }
let foo = undefined;


function init() {
  attachGesture("btn_click", _callback);
  attachGesture("btn_click", _callback);
}

function attachGesture(sElementId, fnFunction) {
  var oElement = document.getElementById(sElementId);
  foo = !foo ? ()=>{console.log('foo!'); fnFunction()} : foo;
  oElement.addEventListener("click", foo);
}

window.onload = init;
<input type="button" id="btn_click" value="the button">

另外,这是working example :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多