01-移除事件监听,除了Removing Event Listeners 你还知道什么?

3,150 阅读3分钟

为了优化性能,我们常见的做法之一、就是当我们不再需要的某些事件的时候,移除不必要的事件监听。

那么除了removeEventListener,你还能想到别的方式么?

下面介绍其他几种方式、让我们在合适的时候,使用合适的方法

通常我们这样绑定一个事件

<button id="button">Do Something</button>

<script>
document.getElementById('button').addEventListener('click'() => {
 console.log('clicked!');
});
</script>

用getEventListeners()我们能看到 元素上绑定的事件

如果此时,需要移除这个事件监听,可以考虑如下的做法

一、removeEventListener

removeEventListener()方法接受三个参数:

  • 要删除的侦听器类型
  • 该侦听器的回调函数
  • 选项对象。

这些参数必须与设置侦听器时使用的参数完全匹配,包括内存中对回调的相同引用。否则 removeEventListener()不会执行任何操作

所以下面这个写法是无效的

document.getElementById('button').addEventListener('click'() => {
 console.log('clicked!');
});

document.getElementById('button').removeEventListener('click'() => {
 console.log('clicked!');
});

虽然两个函数看上去一样,但是因为不是一个引用,所以不能移除掉,可以将回调函数设置为一个变量,如下

const myCallback = () => {
  console.log('clicked!');
};

document.getElementById('button').addEventListener('click', myCallback);
document.getElementById('button').removeEventListener('click', myCallback);

或者用如下方式

document
  .getElementById('button')
  .addEventListener('click'function myCallback() {
    console.log('clicked!');

    this.removeEventListener('click', myCallback);
  });

二、addEventListener() 搭配 once

addEventListener 的第三个参数中,可以通过设置一个 once: true 选项,让函数只调用一次。

const button = document.getElementById('button');

button.addEventListener('click'() => {
 console.log('clicked!');
}, { oncetrue });

// 'clicked!'
button.click();

// No more listeners!
getEventListeners(button) // {}

这样,还可以使用匿名函数、自动在调用后删除。

三、克隆 或 替 换节点

有时候,我们不知道节点上所有的监听函数,但是我们想要把他们全部都移除掉,在这种情况下,可以克隆整个节点,并将其替换为克隆节点

使用.cloneNode()方法,将不会传递通过.addEventListener()连接的监听函数

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

在现代浏览器中,可以使用replaceWith()来简化

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

但是通过'onclick'绑定的事件,通过克隆仍然是存在。

<button id="button" onclick="console.log('clicked!')">
 Do Something
</button>

总之,你想暴力的移除所有的事件监听,这个是一个不错的方法。

四、AbortController()

之前只是听说过,AbortController用于取消fetch()请求,但是显然它更加灵活。

最近,.addEventListener()可以搭配 signal 强制终止事件监听

来自MDN:AbortController 接口的只读属性 signal 返回一个 AbortSignal 实例对象,该对象可以根据需要处理 DOM 请求通信,既可以建立通信,也可以终止通信。

const button = document.getElementById('button');
const controller = new AbortController();
const { signal } = controller;

button.addEventListener('click'() => console.log('clicked!'), { signal });

// Remove the listener!
controller.abort();

它可以在不使用 .removeEventListener() 的情况下移除事件,还有一个更加高效的方式是 使用一个 signal 移除多个事件监听,匿名函数也可以。

const button = document.getElementById('button');
const controller = new AbortController();
const { signal } = controller;

button.addEventListener('click'() => console.log('clicked!'), { signal });
window.addEventListener('resize'() => console.log('resized!'), { signal });
document.addEventListener('keyup'() => console.log('pressed!'), { signal });

// Remove all listeners at once:
controller.abort();

但是,这个是一个较新的功能,自2021(v90)以来,Chrome才完全支持它,所以了解即可,如果要支持多数浏览器,还是慎用。

总结一下

  • 如果监听函数是一个具名函数,或者保存在一个变量内,可以使用removeEventListener
  • 如果只触发一次回调,可以使用 addEventListener() 搭配 once
  • 如果无差别的删除多个事件,可以使用 克隆 或 替 换节点
  • 如果有一系列要立即强制删除的事件监听,或者只是想尝试一下新语法,可以试试AbortController()。

翻译原文:www.macarthur.me/posts/optio…