通过addEventListener添加的事件侦听器被多次调用

时间:2018-12-03 11:55:43

标签: javascript

我正在使用addEventListener来响应某些事件。

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

这是MDN的报价:

  

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

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仅被调用一次?

3 个答案:

答案 0 :(得分:2)

  

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

不是。

如果多次注册 same 事件处理程序,则只会调用一次。

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

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

答案 1 :(得分:1)

As Quentin already mentioned and is written in the MDN documentation, the event listener functions must be identical to do what you want to achieve.

(emphasize mine)

If multiple identical EventListeners are registered on the same EventTarget with the same parameters, the duplicate instances are discarded.

I tried to use Function.prototype.bind to achieve this, but that returns a copy of the original function and so won't work.

I think you cannot do it without doing your own housekeeping.

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

答案 2 :(得分:1)

Based on what I've read, since you're recreating foo inside attachGesture every time you're calling it, the above-mentioned function foo is pertaining to a different instance of itself every time. Thus, they are not the same (which violates what was mentioned in your MDN reference). Storing a reference to foo somewhere and assigning that one instead should fix the problem.

Simply put, you could try something like this

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

Also, here's a working example :)

相关问题