addEventListener

22 阅读5分钟

一、DOM 事件流的三个阶段

一个完整的 DOM 事件流,严格来说分为三个阶段:

1. 捕获阶段 (Capture Phase)
  • 方向由外向内,从 window -> document ->  ->  -> ... -> 目标元素的父元素。
  • 目的:让外层元素有机会在内层元素之前“拦截”并处理事件。
  • 工作机制:浏览器会从最外层的祖先元素开始,检查它是否注册了捕获阶段的事件监听器。如果有,就执行它,然后继续向内层元素移动,重复此过程,直到到达事件的目标元素。
  • 默认行为默认情况下,我们用 addEventListener 绑定的事件监听器是不会在捕获阶段执行的。
2. 目标阶段 (Target Phase)
  • 方向:事件到达真正被触发的那个元素(即事件目标,event.target)。
  • 工作机制:浏览器执行注册在该目标元素上的事件监听器。
  • 一个微妙的点:在目标阶段,事件并没有一个明确的“方向”。你可以认为它是捕获阶段的终点,也是冒泡阶段的起点。在这个阶段触发的监听器,无论是注册为捕获还是冒泡,都会被执行。
3. 冒泡阶段 (Bubbling Phase)
  • 方向由内向外,从事件目标开始,逐级向父元素传播,直到 window 对象。

  • 目的:让内层元素的事件,能够被外层元素“感知”到。这是最常用、最符合直觉的阶段。

  • 工作机制:从目标元素开始,浏览器检查它是否注册了冒泡阶段的事件监听器。如果有,就执行它,然后移动到其父元素,重复此过程,直到最外层的祖先元素。

  • 默认行为我们平时用 addEventListener(type, listener) 绑定的事件,默认就是在冒泡阶段执行的。

    事件流的控制与应用

1. 阻止传播
  • event.stopPropagation() :

    • 作用:在当前阶段执行完所有监听器后,阻止事件继续传播到下一个元素(无论是捕获阶段的“向下”,还是冒泡阶段的“向上”)。
    • 比喻:军官收到士兵的战报后,决定“此事到此为止,不必再上报给将军了”。
  • event.stopImmediatePropagation() :

    • 作用:一个更“霸道”的版本。它不仅会阻止事件向下一个元素传播,还会阻止在当前元素上、注册在它之后的任何同类型事件监听器被执行。
    • 比喻:军官收到战报后,不仅不上报,还命令手下其他传令兵“你们也都别动了,这事我一个人处理就行”。
2. 阻止默认行为
  • event.preventDefault() :

    • 作用:阻止浏览器对该事件的默认行为。

    • 例子

      • 阻止 a 标签的点击跳转。
      • 阻止 form 的提交按钮刷新页面。
      • 阻止复选框的点击选中/取消选中。
    • 注意:它不会阻止事件的传播。

3. 事件委托 (Event Delegation)
  • 这是事件冒泡阶段最重要、最强大的应用模式。
  • 通过在父元素上监听事件,来统一处理所有子元素的事件。这极大地提升了性能,并能方便地为动态添加的子元素绑定事件。

addEventListener的用法

target.addEventListener(type, listener, options);

type 表示触发事件的类型 比如 click

listener 事件触发之后执行的回调函数。

listener如果是一个普通函数,普通函数中的this会被显示(call)的绑定到触发事件的dom上 listener如果是一个箭头函数,箭头函数中的this会绑定到全局对象上(call修改this指向,对箭头函数没用)

这个回调函数默认接收一个event参数

这个 event 对象包含了关于该事件的所有信息,比如:
-   event.target: **真正触发**事件的那个最深层的元素。
-   event.currentTarget: **当前正在处理**事件的元素(即你调用 addEventListener 的那个 target)。
-   event.preventDefault(): 阻止事件的默认行为。
-   event.stopPropagation(): 阻止事件继续在 DOM 树中传播(冒泡或捕获)。
-   event.clientX, event.clientY: 鼠标事件的坐标。
-   event.key: 键盘事件的按键值。

options (对象,可选) 或 useCapture (布尔值,可选)

这个参数用于配置监听器的行为,非常重要。你可以传入一个布尔值,或者一个更灵活的选项对象。

  • useCapture (布尔值)  - 历史遗留用法

    • false (默认值): listener 在冒泡阶段执行。
    • true: listener 在捕获阶段执行。
  • options (对象)  - 现代推荐用法
    这个对象可以包含以下属性:

    • capture: boolean:

      • 与 useCapture 完全相同。true 表示在捕获阶段监听。
    • once: boolean:

      • 如果为 true,表示这个 listener 最多只会被触发一次。触发后,它会自动被移除。
      • 非常有用!避免了手动调用 removeEventListener 的麻烦。
      • 示例:一个“欢迎”动画,只需要在页面首次加载时播放一次。
    • passive: boolean:

      • 这是一个性能优化选项,主要用于触摸 (touch)  和滚轮 (wheel)  事件。
      • 当你设置为 true 时,你等于在向浏览器承诺:“**我的这个 listener 函数内部,绝对不会调用 event.preventDefault() 来阻止默认行为(比如滚动)。** ”
      • 好处:浏览器收到这个承诺后,就可以不必等待你的 listener 执行完毕,而是可以立即开始处理滚动,从而让页面滚动变得更加流畅,减少卡顿。对于提升移动端的滚动性能至关重要。
    • signal: AbortSignal:

      • 用于中止/移除事件监听器。它与 AbortController API 配合使用。