gpt4 book ai didi

javascript - 无论回调是如何定义的,如何删除事件监听器

转载 作者:行者123 更新时间:2023-11-30 11:12:09 25 4
gpt4 key购买 nike

多年来,我一直在尝试删除 JavaScript 中的事件监听器时遇到问题。通常我必须创建一个独立的函数作为处理程序。但这太草率了,尤其是在添加了箭头功能的情况下,简直是一种痛苦。

I am not after a ONCE solution. This needs to work in all situations no matter HOW the callback is defined. And this needs to be raw JS so anyone can use it.

下面的代码工作正常,因为函数 clickHandler 是一个独特的函数,可以被 addEventListenerremoveEventListener 使用:

This example has been updated to show what I have run into in the past

const btnTest = document.getElementById('test');
let rel = null;

function clickHandler() {
console.info('Clicked on test');
}

function add() {
if (rel === null) {
rel = btnTest.addEventListener('click', clickHandler);
}
}

function remove() {
btnTest.removeEventListener('click', clickHandler);
}

[...document.querySelectorAll('[cmd]')].forEach(
el => {
const cmd = el.getAttribute('cmd');
if (typeof window[cmd] === 'function') {
el.addEventListener('click', window[cmd]);
}
}
);
<button cmd="add">Add</button>
<button cmd="remove">Remove</button>
<button id="test">Test</button>

您过去可以使用 arguments.callee 来做到这一点:

var el = document.querySelector('#myButton');

el.addEventListener('click', function () {
console.log('clicked');
el.removeEventListener('click', arguments.callee); //<-- will not work
});
<button id="myButton">Click</button>

但是使用箭头函数是行不通的:

var el = document.querySelector('#myButton');

el.addEventListener('click', () => {
console.log('clicked');
el.removeEventListener('click', arguments.callee); //<-- will not work
});
<button id="myButton">Click</button>

有没有更好的办法??

更新

正如@Jonas Wilms 所述,这种方式可行:

 var el = document.querySelector('#myButton');

el.addEventListener('click', function handler() {
console.log('clicked');
el.removeEventListener('click', handler); //<-- will work
});
<button id="myButton">Click</button>

除非您需要使用绑定(bind):

var obj = {
setup() {
var el = document.querySelector('#myButton');

el.addEventListener('click', (function handler() {
console.log('clicked', Object.keys(this));
el.removeEventListener('click', handler); //<-- will work
}).bind(this));
}
}

obj.setup();
<button id="myButton">Click</button>

问题是有太多方法可以为 addEventListener 函数提供事件处理程序,如果您传入函数的方式在重构中发生变化,您的代码可能会中断。

最佳答案

您不能直接使用箭头函数或任何匿名函数并期望能够删除监听器。

要删除监听器,您需要将 EXACT SAME ARGUMENTS 传递给 removeEventListener,就像传递给 addEventListener 一样,但是当您使用匿名函数或一个箭头函数,您无权访问该函数,因此您不可能将其传递给 removeEventListener

作品

const anonFunc = () => { console.log("hello"); }
someElem.addEventListener('click', anonFunc);
someElem.removeEventListener('click', anonFunc); // same arguments

不工作

someElem.addEventListener('click', () => { console.log("hello"); });    
someElem.removeEventListener('click', ???) // you don't have a reference
// to the anon function so you
// can't pass the correct arguments
// to remove the listener

你的选择是

  • 不要使用匿名函数或箭头函数
  • 使用包装器为您跟踪参数

一个例子是@Intervalia 闭包。他跟踪您传入的函数和其他参数,并返回一个函数,您可以使用该函数移除监听器。

我经常使用的一个解决方案经常满足我的需要,它是一个跟踪所有监听器并将它们全部删除的类。它返回一个 id 而不是闭包,但它也允许只删除所有监听器,我发现这在我现在构建一些东西并想稍后拆除它时很有用

function ListenerManager() {
let listeners = {};
let nextId = 1;

// Returns an id for the listener. This is easier IMO than
// the normal remove listener which requires the same arguments as addListener
this.on = (elem, ...args) => {
(elem.addEventListener || elem.on || elem.addListener).call(elem, ...args);
const id = nextId++;
listeners[id] = {
elem: elem,
args: args,
};
if (args.length < 2) {
throw new Error('too few args');
}
return id;
};

this.remove = (id) => {
const listener = listeners[id];
if (listener) {
delete listener[id];
const elem = listener.elem;
(elem.removeEventListener || elem.removeListener).call(elem, ...listener.args);
}
};

this.removeAll = () => {
const old = listeners;
listeners = {};
Object.keys(old).forEach((id) => {
const listener = old[id];
if (listener.args < 2) {
throw new Error('too few args');
}
const elem = listener.elem;
(elem.removeEventListener || elem.removeListener).call(elem, ...listener.args);
});
};
}

用法类似于

const lm = new ListenerManager();
lm.on(saveElem, 'click', handleSave);
lm.on(newElem, 'click', handleNew);
lm.on(plusElem, 'ciick', handlePlusOne);
const id = lm.on(rangeElem, 'input', handleRangeChange);

lm.remove(id); // remove the input event on rangeElem

lm.removeAll(); // remove events on all elements managed by this ListenerManager

请注意上面的代码是 ES6 并且必须更改以支持真正的旧浏览器,但想法是相同的。

关于javascript - 无论回调是如何定义的,如何删除事件监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53266000/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com