Shishigami
Shishigami

Reputation: 451

Event listener that is added via addEventListener is being called multiple times

I am using addEventListener to react to certain events.

From what I read, it is possible to register multiple identical EventListeners on the same EventTarget, and the EventListener will only be called once.

Here's the quote from MDN:

If multiple identical EventListeners are registered on the same EventTarget with the same parameters, the duplicate instances are discarded. They do not cause the EventListener to be called twice, and they do not need to be removed manually with the removeEventListener() method. Note however that when using an anonymous function as the handler, such listeners will NOT be identical since anonymous functions are not identical even if defined using the SAME unchanging source-code simply called repeatedly, even if in a loop. However, repeatedly defining the same named function in such cases can be more problematic. (see Memory issues below.)

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

I am not sure why this does not happen in this example? Running this code will call the EventListener twice: (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;

How can I make sure the EventListener is called only once?

Upvotes: 2

Views: 2982

Answers (3)

C.RaysOfTheSun
C.RaysOfTheSun

Reputation: 716

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 :)

Upvotes: 2

yunzen
yunzen

Reputation: 33449

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>

Upvotes: 2

Quentin
Quentin

Reputation: 944568

it is possible to register multiple identical EventListeners on the same EventTarget, and the EventListener will only be called once.

It isn't.

If you register the same event handler multiple times, then it will only be called once.

Identical but not === event handlers are not the same event handler.

You need to change your logic to avoid attempting to register identical handlers.

Upvotes: 3

Related Questions