js:移除或禁用之前添加的某个事件的事件监听器

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

codepen

看下面的例子(和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>

预期输出为 b

在我的原始代码中,我有多个事件处理程序,例如 clickmousemove 等。有没有一种方法可以删除某个特定事件的所有事件侦听器?以前添加过?

例如,在开始时,click到我的按钮触发function A;在中间,我考虑需要一个功能上的改变,所以我不想触发 function A,而是 function B,并且只需要 function B.

我已阅读:

但这并没有多大帮助。感谢您的帮助。


更新

更具体地说,有没有办法阻止以前添加的 click 的事件侦听器在新添加的函数中,例如:


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

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


解决方案

感谢@Kayac,解决了这个问题:

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

更新

对于访问者,如果您正在寻找 html 选择器以允许您在事件侦听器上执行单例,则以下代码已针对性能进行了调整。

/* 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));
}

您需要通过引用移除之前的事件侦听器: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

试试这个:

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

这应该只有 运行 b

你试过吗?

removeEventListener

它删除了一个用 addEventListener() 方法附加的“点击”事件:

例子

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

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

试试这个

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

恐怕您必须自己使用 addEventListener 跟踪您附加到元素的事件。没有 built-in 方法来查询这些。 要使用 removeEventListener 成功删除事件,它必须具有与调用 addEventListener 相同的签名。

这是通过使用命名函数而不是匿名函数来完成的。

例如:

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>

没有直接的方法可以做到这一点。

但是,如果你控制了所有的监听器的添加和移除,你可以收集监听器并全部移除,就像这样:

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>