Moe007
Moe007

Reputation: 68

Remove event listener where handler is not in scope

I'm in a situation where I have an EventListener on two elements, and they have the same handler. The EventListeners are added inside of a function and the handler takes in arguments.

When one of these elements are clicked the EventListeners on both elements should be removed.

Example code:

const handler = (e, x, otherEle) => {
    e.target.removeEventListener('click', callHandler)
    otherEle.removeEventListener('click', callHandler)
    //callHandler is not defined
}

const myFunc = ()=>{
   let x = 7
   let ele1 = document.querySelector("#ele1")
   let ele2 = document.querySelector("#ele2")

   ele1.addEventListener('click', function callHandler(event){
     handler(event, x, ele2)
   })

   ele2.addEventListener('click', function callHandler(event){
    handler(event, x, ele1)
   })
}

I've tried to pass the function callHandler as an argument but that did not work.

Upvotes: 1

Views: 745

Answers (1)

skyline3000
skyline3000

Reputation: 7913

There are many strategies you could use to solve this issue. The simplest may be the fact that references to elements with IDs are already available in the global scope, so you can reference them as regular variables or from the window object:

const handler = () => {
    console.log("calling handler only once");
    ele1.removeEventListener('click', handler)
    ele2.removeEventListener('click', handler)
}

const myFunc = () => {
   // These references to element IDs are already available in the global scope
   // let ele1 = document.querySelector("#ele1")
   // let ele2 = document.querySelector("#ele2")

   ele1.addEventListener('click', handler);
   ele2.addEventListener('click', handler);
}

myFunc();
div {
  border: 1px solid black;
  height: 50px;
  width: 50px;
}
<div id="ele1"></div>
<div id="ele2"></div>

Another option is to simply use onclick rather than adding and removing event listeners which will work the same way (it only prevents multiple click events on an element from being listened to):

const handler = (e, x, otherEle) => {
    console.log("calling handler only once");
    e.target.onclick = null;
    otherEle.onclick = null;
}

const myFunc = ()=>{
   let x = 7
   let ele1 = document.querySelector("#ele1")
   let ele2 = document.querySelector("#ele2")

   ele1.onclick = function (event) {
     handler(event, x, ele2)
   }

   ele2.onclick = function (event) {
     handler(event, x, ele1)
   }
}

myFunc();
div {
  border: 1px solid black;
  height: 50px;
  width: 50px;
}
<div id="ele1"></div>
<div id="ele2"></div>

Another option, though perhaps the trickiest to implement, is to pass references to the elements in your function calls by using something like .bind() The scopes get a bit unwieldy, I'm not sure it's achievable with your arrow functions.

ele1.addEventListener('click', handler.bind(this, ele2, x))

Upvotes: 1

Related Questions