removeEventListener

最常见也是使用最多的写法,addEventListener 之后 ,在合适的时机通过 removeEventListener API 来移除事件监听器。
需要注意的是 removeEventListener的参数必须与addEventlistener的参数完全匹配,包括内存中回调的相同引用,否则 removeEventListener 无效。

Bad:

window.addEventListener('scroll', () => {});
 
window.removeEventListener('scroll', () => {});

Good:

fcuntion onScroll() {};
 
window.addEventListener('scroll', onScroll);
 
window.removeEventListener('scroll', onScroll);

对于特定用例,还可以通过函数本身引用伪匿名函数来删除监听器:

document.getelementById('btn').addEventListener('click', function callback() {
	this.removeEventListener('click', callback);
})

addEventListener 的 once 选项

addEventListener 带有一个 once 选项,如果你的事件监听器是一次性使用的话,它可以帮助清理自身

document.getElementById('btn').addEventListener('click', () => {
	console.log('click');
}, { once: true });

once 设置为 true 时,在事件监听器的回调第一次执行过后,会自动移除事件监听。

cloneNode 和 replaceWith

这种方式简单来说就是克隆目标节点,并且将克隆出来的目标节点再替换自身。因为通过 cloneNode 克隆出来的节点,不会复制原节点上的事件监听,是一个相对干净的节点。这种方式试用移除某个节点上的所有事件监听器,而不是单单移除某个事件监听,例如

button.parentNode.replaceChild(button.cloneNode(true), button);

试用新的replaceWith 方法可以简化这一流程

button.replaceWith(button.cloneNode(true));

这种方式非必要尽量不要使用,会暴力移除事件监听,有点黑魔法的味道

AbortController

在原来的认知中,AbortController 是用来中断 fetch 请求的,例如处理竞态请求时 竞态请求的处理,就利用了 AbortController 的特性。但是 AbortController 还可以用来移除事件监听器,并且比removeEventListener 更好

const onResize = () => {};
 
const controller = new AbortController();
 
// add
window.addEventListener('resize', onResize, { signal: controller.signal });
 
// remove
controller.abort();
 

这种方式除了看起来更加语义化之外,还有一个更大的好处,就是使用一个 signal 删除任何类型的多个事件监听器,哪怕是匿名函数也能正常使用:

const controller = new AbortController();
 
window.addEventListener('scroll', () => {}, { signal: controller.signal });
document.getElementById('btm', () => {}, { signal: controller.signal });
window.addEventListener('resize', () => {}, { signal: controller.signal });
 
//remove all
controller.abort();

这种方式使用起来更加简单和方便,但是需要注意的是,AbortController 是一个相对较新的 API,如果你的项目需要兼容旧版本的浏览器,应该慎重使用

image.png