🍉巧妙地移除 JS 事件监听 - 后端大佬点了个赞

3 阅读4分钟

移除事件监听可以说是 JavaScript 的最基础知识点之一。在页面开发中,一般会和注册事件监听配对出现,组件渲染时注册事件,组件销毁时移除事件。

最近碰到了一个小问题 - 关于如何移除第三方网站事件监听,借这个机会重新巩固了下事件机制这块知识点。

场景复现

日常写代码的一天,后边的后端大佬突然喊道:“前端小哥,有没空帮忙搞下这个,这个测评输入框不让粘贴,我搞了半小时还是不能粘贴。”

作为前端,第一直觉就是输入框对 paste 事件做了处理,先查找节点的事件。一顿操作,最后在输入框父元素找到了对应的 paste 事件:

item.addEventListener('paste', (e) => {
  e.preventDefault()
})

那么问题来了,如何去移除第三方网站的一些监听事件?

我们都知道事件监听的注册有两种方式(以粘贴事件为例):

  • element.onpaste = () => {}
  • element.addEvenetListener('paste', handler)

对应的移除监听是:

  • element.onpaste = null
  • element.removeEventListener('paste', hanlder)

自己的开发项目中移除事件监听比较容易,要禁止第三方网站事件监听,不让其生效的话,我们可以尝试以下方式。

解决方案

浏览器禁止 JS

当你设置浏览器禁止使用 JavaScript 的时候,事件监听就不会在生效了

chrome 浏览器可以通过以下来设置:设置 - 隐私和安全 - 网站设置 - JavaScript

QQ图片20240403124621.png

但是这种方式限制很大,你可能都无法打开测评问卷页面,现在大多网站都是单页应用,HTML 只有一个 root 节点,全靠 JS 渲染,所以不考虑。

Chrome 开发工具删除事件

打开你的开发者工具,选中对应的元素,在右侧事件监听器中可以找到当前节点的所有事件,点击“移除”即可。

image.png

我们成功地移除了事件监听,可以正常粘贴了,但是测评问卷的题目很多,不可能一个个手动去移除,还是需要继续看是否有更好的方法。

捕获阶段禁止

JavaScript 事件机制有两个阶段:捕获和冒泡,大部分情况下我们都是使用的冒泡机制,这边我们可以在 body 元素上设置一个捕获阶段监听,并阻止事件继续传递,这样 paste 事件在最开始就被禁止掉了。

document.body.addEventListener('paste', (event) => {
    event.stopPropagation()
}, true) // true 设置为捕获阶段监听

执行上述代码后,输入框的粘贴功能又正常可以使用了。

使用 getEventListeners 和 removeEventListener

使用 removeEventListener 是最常规的方法,问题在于要怎么获取到监听事件函数,如果不是注册的那个监听函数,我们就无法移除事件监听。

在资料搜索中,发现 chrome 开发者工具提供了一个 api getEventListeners,使用这个可以获取到某个元素的所有监听事件,那我们就可以使用 removeEventListener 移除掉 paste 事件。记住,这个是 Chrome 开发者工具提供的 API,在我们的 JS 代码中无法使用。

QQ截图20240403130928.png

document.body.querySelectorAll('.item').forEach(item => {
    item.removeEventListener('paste', getEventListeners(item)?.paste?.[0]?.listener)
})

这种方式让页面输入框恢复粘贴能力也很方便。

拓展知识

匿名函数的移除监听

我们都知道要移除监听的事件,我们的监听函数必须保证是注册时的那个,函数引用必须相同,即匿名函数无法通过 removeEventListener 移除。

但是 AbortController API 让我们可以移除匿名函数的监听,使用方法如下:

const controller = new AbortController();
const { signal } = controller;

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

element.addEventListener('mouseover', () => {
  console.log('hovered');
}, { signal });

controller.abort() // 取消监听

AbortController API 的更多用法可以查看:AbortController AbortSignal 的使用

总结

移除事件监听是 JavaScript 中的一个基础知识点,我们要掌握它并能在实际中运用。最后除了以上方法,还有 cloneNode 方法也用来可以移除事件监听,有兴趣的同学可以试试。

如果你有更好的方法,欢迎留言讨论。

MDN 相关学习资料:事件介绍