Js: remove or disable previous added event listeners of a certain event

4.2k Views Asked by At

codepen

Take following example (same as codepen)

const div = document.querySelector('div');

function a(){
  console.log('a');
}

function b(){
  console.log('b');
}

div.addEventListener('click', e=>{
  a();
});

div.addEventListener('click', e=>{
  // I don't want all previous added event handlers to work, is there a way to remove it?
  b();
});
div{
  width: 200px;
  height: 200px;
  background: red;
}
<div></div>

the expected output is b;

In my original code, I have more than one event handlers, such as click, mousemove, etc. is there a way I can remove all event listeners of a particular event that I previously added?

For example, at the start, click to my button triggers function A; in the middle, I consider needing a functional change, so instead of triggering function A, I want function B, and only function B.

I have read:

but it didn't help much. thanks for your help.


update

To be more specific, is there a way to prevent previously added event listeners of click inside the new added function, for instance:


my_button.addEventListener('click', ()=>{
    console.log('a');
});

my_button.addEventListener('click', ()=>{
    // remove all previously added click handlers 
    console.log('b');
});


solution

Thanks to @Kayac, worked this out:

// add a new method to element, oneEventListener, 
// for a speficified event type, only save and execute last add handler

function elemental(el){
    el.oneEventListener = (event, func) => {
        if(el.lastEventListener == null){
          el.lastEventListener = {};
        }
        if(el.lastEventListener[event] != null){
          el.removeEventListener(event, el.lastEventListener[event]);
        }
        el.addEventListener(event, func);
        el.lastEventListener[event] = func;
    }
    return el;
}

// eventhandler singleton, support querySelector, getElementById, etc, even support querySelectorAll and getElementsByClassName
function proxy(el) {
  if(!(el instanceof NodeList)){
    return elemental(el);
  }else{
    el.forEach(ele=>{
      ele = elemental(ele);
    });
    return el;
  }
}

const div = proxy(document.querySelector('div'));
// const div = document.querySelector('div');

div.oneEventListener('click', e=>{
    console.log('a');
})

div.oneEventListener('click', e=>{
    console.log('b');
})

div.oneEventListener('click', ()=>{
    console.log('c');
})

div.oneEventListener('mouseover', ()=>{
    console.log('aa');
})

div.oneEventListener('mouseover', ()=>{
    console.log('bb');
})

div.oneEventListener('mouseover', ()=>{
    console.log('cc');
})
div{
  width: 200px;
  height: 200px;
  background: red;
}
<div></div>

Update

For visitors, if you are looking for html selector to allow you to perform singleton on event listener, following code is tuned for performance.

/* html element selector */

// element: one event maps to one handler
function elemental(el){
  el.oneEventListener = (event, func) => {
    if(el.lastEventListener == null){
      el.lastEventListener = {};
    }
    if(el.lastEventListener[event] != null){
      el.removeEventListener(event, el.lastEventListener[event]);
    }
    el.addEventListener(event, func);
    el.lastEventListener[event] = func;
  }
  return el;
}

// querySelector -> element
function proxy(el){
  return elemental(el);
}

// querySelectorAll -> NodeList
function proxyAll(el){
  el.forEach(ele=>{
    ele = elemental(ele);
  });
  return el;
}

/*
once proxy is implemented, by oneEventListener, 
only one event listener of particular type will exist on that element,
if you call addEventListener, proxy doesn't interfer it.
which the eventlistener added will work normally.
*/

// proxy query selector
const pqs = function(str){
  return proxy(document.querySelector(str));
}

// proxy query selector all
const pqsa = function(str){
  return proxyAll(document.querySelectorAll(str));
}
5

There are 5 best solutions below

2
On BEST ANSWER

Try this

function a() {
  console.log("a");
}

function b() {
  console.log("b");
}

function proxy(el) {
  const map = {};

  return {
    addEventListener: (event, func) => {
      !map[event] && el.addEventListener(event, () => map[event]());

      map[event] = func;
    }
  };
}

const div = proxy(document.querySelector("div"));

div.addEventListener("click", a);
div.addEventListener("click", b);
2
On

There's no straightforward way to do that.

However, if you control all the listener additions and removals, you can collect the listeners and remove all, like this:

const elements = new WeakMap

function customAddEventListener(elem, event, fn){
  elem.addEventListener(event, fn)

  if(!elements.has(elem)) elements.set(elem, Object.create(null))
  const events = elements.get(elem)
  if(!(event in events)) events[event] = new Set
  const fns = events[event]

  fns.add(fn)
}
function customRemoveEventListener(elem, event, fn){
  elem.removeEventListener(event, fn)

  if(!elements.has(elem)) return
  const events = elements.get(elem)
  if(!event in events) return
  const fns = events[event]

  fns.delete(fn)

  if(!fns.size) delete events[event]
  if(!Reflect.ownKeys(events).length) elements.delete(elem)
}

function removeAllEventListeners(elem, event){

  if(!elements.has(elem)) return
  const events = elements.get(elem)
  if(!event in events) return
  const fns = events[event]

  for(const fn of fns)
    elem.removeEventListener(event, fn)

  delete events[event]
  if(!Reflect.ownKeys(events).length) elements.delete(elem)
}


const div = document.querySelector('#div');

function a(){
  console.log('a');
}

function b(){
  console.log('b');
}

customAddEventListener(div, 'click', e=>{
  a();
});

customAddEventListener(div, 'click', function fn(e){
  b();
  removeAllEventListeners(div, 'click')
  //add this one back
  customAddEventListener(div, 'click', fn)
});
#div{
  width: 200px;
  height: 200px;
  background: red;
}
<div id="div"></div>

0
On

You need to remove the previous event listener by reference: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

try this:

const div = document.querySelector('div');

function a(){
  console.log('a');
}

function b(){
  console.log('b');
}

div.addEventListener('click', a);

// condition for changing event listener
if (true) {
    div.removeEventListener('click', a);
}

div.addEventListener('click', b);

This should only run b

2
On

Did you try this?

removeEventListener

It removes a "click" event that has been attached with the addEventListener() method:

Example

// Attach an event handler to <div>
document.getElementById("myDIV").addEventListener("click", myFunction);

// Remove the event handler from <div>
document.getElementById("myDIV").removeEventListener("click", myFunction);
0
On

I'm afraid you have to keep track of the events you're attaching to an element using addEventListener yourself. There's no built-in method to query those. To successfully remove an event using removeEventListener, it must have the same signature as the call to addEventListener.

This is done by using a named function instead of an anonymous function.

For example:

var myButton = document.getElementById("myButton");
var myButton2 = document.getElementById("myButton2");

function a() {
  console.log("function a");
}

function b() {
  console.log("function b");
}

function changeFunction() {
  myButton.removeEventListener('click', a);
  myButton.addEventListener('click', b);
}
myButton.addEventListener('click', a);
myButton2.addEventListener('click', changeFunction);
<button id="myButton">
click me
</button>
<button id="myButton2">
attach new listener
</button>