addEventListener 劫持
本章中我们将学习到如何使用 addEventListener 劫持。
背景:捕获与冒泡阶段
假 设现在有一个 html
<div class="div1">
<div class="div2">
<div class="div3"></div>
</div>
</div>
当我们点击 div3 的时候,会如下进行流程:
-
依次触发
div1>div2中捕获监听函数 -
触发
div3的捕获和冒泡监听,此时不再区分点击元素的捕获和监听,通常按addEventListener调用的先后顺序处理 -
依次触发
div2>div1中冒泡监听函数
addEventListener 使用
addEventListener 劫持通常用于解决网页对于外部状态的检测,如视频检测到最小化页面就不播放,鼠标离开固定区域就会提示等等。
这个时候我们可以利用 addEventListener 劫持对特定的监听事件实现过滤,从而使对应的功能失效或监听触发等等。
-
监听方式一般有两种,
- 通过 onClick 函数等触发调用
- 使用
addEventListener进行监听
-
目前网页主流使用
addEventListener,其原因在于:on系列的监听函数相对性能较差addEventListener支持添加多个函数
关于 addEventListener 我们可以查询MDN 文档
addEventListener 来自 EventTarget 这个对象下的原型中,通常来说我们的 dom 元素等也都是继承自这个对象的原型链
所以我们可以写出以下劫持代码
const oldEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (...args) {
console.log("addEventListener Hook", this, ...args);
return oldEventListener.call(this, ...args);
};
addEventListener 的语法
addEventListener 一共有三种调用方式
addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);
addEventListener 的参数
-
type事件监听类型
关于事件我们可以在菜鸟教程查询到
提示在
addEventListener使用不需要带on前缀 -
listener触发函数,当我们传入的事件监听类型被网页监听到,网页就会调用这个函数
-
useCapture和options-
useCapture布尔值,为
true时,在捕获阶段触发回调函数,为false时,在冒泡阶段触发回调函数 -
optionsoptions有四个参数,capture,once,passive和signal参数 说明 capture布尔值,在捕获阶段触发回调函数,该选项与 useCapture等价once布尔值,为 true时,在调用一次后自动移除passive布尔值,为 true时,承诺回调函数不会调用preventDefault,这样确保了回调函数的逻辑一定不阻止默认操作,从而无需等待回调函数告知是否阻止操作即可执行,提升网页的性能,改善体验。signal传入一个信号对象,等信号对象调用 abort函数后,addEventListener将被自动移除
-
listener 的一些常用函数
listener 作为一个回调函数,当监听的对应事件触发时会调用该函数,同时传入一个 Event 对象,具体的内容可以参考MDN,我们这里仅挑出一些常用的函数进行讲解。
preventDefault
取消默认行为的执行,如一个 a 标签,点击后会自动跳转网页,如果我们调用如下代码,即不会再跳转网页。
并非所有活动都可以取消。可以通过 cancelable 属性来确定事件是否可取消。
document.querySelector("a").addEventListener("click", (event) => {
event.preventDefault();
});
stopPropagation
阻止后续传播。依然以之前的html为例:
<div class="div1">
<div class="div2">
<div class="div3"></div>
</div>
</div>
从捕获到冒泡的顺序依次是
div1 捕获 > div2 捕获 > div3 捕获和冒泡 > div2 冒泡 > div1 冒泡
这个时候如果在 div2 捕获时调用 stopPropagation,在 div2 捕获的监听器全部执行完毕后,后续的div3捕获和冒泡,div2冒泡,div1冒泡都将不再执行。
在当前元素调用 stopPropagation 后,依然会执行完该元素的所有监听器后停止,
例如:
div2 设置了两个捕获监听器,
在第一个调用了 stopPropagation 后,第二个监听器依然会执行完毕。
stopImmediatePropagation
如果你理解了 stopPropagation,那么 stopImmediatePropagation 也很好理解,即使调用了 stopPropagation,同级同阶段的监听器依然会全部执行完毕后才停止,而 stopImmediatePropagation 而是调用后立即停止,不再执行后续的同级同阶段的监听器。