浏览器的 EventTarget

37 阅读3分钟

EventTarget

EventTarget这个函数对我来说,既熟悉又陌生。因为它几乎别主要的浏览器接口继承了。我知道的Document,HtmlElement, Node, window 等都继承了它,使用它实现事件的监听收集。

它通常是被继承到doc、element里面,如果我们需要独立使用,需要关注三个部分:

  • 实例化 new EventTarget()
  • 添加事件监听 addEventListener()
  • 派发事件 dispatchEvent()
  • 移除事件监听 removeEventListener()

其中,查阅MDN,认为addEventListener和removeEventListener值得注意

addEventListener()

备注:  推荐使用 addEventListener() 来注册一个事件监听器,理由如下:

  • 它允许为一个事件添加多个监听器。特别是对库、JavaScript 模块和其他需要兼容第三方库/插件的代码来说,这一功能很有用。
  • 相比于 onXYZ 属性绑定来说,它提供了一种更精细的手段来控制 listener 的触发阶段。(即可以选择捕获或者冒泡)。
  • 它对任何事件都有效,而不仅仅是 HTML 或 SVG 元素。
addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);

这里比较陌生的是options:
interface options = {
    capture:boolean // 表示 `listener` 会在该类型的事件捕获阶段传播到该 `EventTarget` 时触发。
    once:boolean // 表示 `listener` 在添加之后最多只调用一次。如果为 `true`,`listener` 会在其被调用之后自动移除。
    passive: boolean // 设置为 `true` 时,表示 `listener` 永远不会调用 `preventDefault()`。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告
    signal: AbortSignal
}
useCapture: boolean // 是否使用捕捉模式,而不是冒泡模式

注意1 - option.capture 或者 useCapture 的使用

当设置option.capture 或者 useCapture 时,在移除监听事件动作时也需要同时指定removeEventListener的option.capture 或者 useCapture。否则,可能出现没有正确移除的问题

// 情况一
element.addEventListener("mousedown", handleMouseDown, true);
element.removeEventListener("mousedown", handleMouseDown, false);     // 失败
element.removeEventListener("mousedown", handleMouseDown, true);      // 成功
// 情况二
element.addEventListener("mousedown", handleMouseDown, { passive: true });
element.removeEventListener("mousedown", handleMouseDown, { passive: true });     // 成功
element.removeEventListener("mousedown", handleMouseDown, { capture: false });    // 成功
element.removeEventListener("mousedown", handleMouseDown, { capture: true });     // 失败
element.removeEventListener("mousedown", handleMouseDown, { passive: false });    // 成功
element.removeEventListener("mousedown", handleMouseDown, false);                 // 成功
element.removeEventListener("mousedown", handleMouseDown, true);                  // 失败

注意2 - option.passive

根据规范,addEventListener() 的 passive 默认值始终为 false。然而,这引入了触摸事件和滚轮事件的事件监听器在浏览器尝试滚动页面时阻塞浏览器主线程的可能性——这可能会大大降低浏览器处理页面滚动时的性能。

为了避免这一问题,大部分浏览器(Safari 和 Internet Explorer 除外)将文档级节点 WindowDocument 和 Document.body 上的 wheelmousewheeltouchstart 和 touchmove 事件的 passive 默认值更改为 true。如此,事件监听器便不能取消事件,也不会在用户滚动页面时阻止页面呈现。

因此,当你想要覆盖这一行为并确认 passive 在所有浏览器中都被设为 false,你必须显式地将其设为 false,而不是依赖浏览器的默认设置。

不过,你无需担心基本 scroll 事件的 passive 值。因为该事件不能被取消,事件监听器也就无法阻止页面的渲染。