浏览器原理 · 1/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

addEventListener 第三个参数

addEventListener 第三个参数的两种形式及使用场景

问题

addEventListener 函数的第三个参数有什么作用?

解答

addEventListener 的第三个参数有两种形式:布尔值配置对象

基本语法

element.addEventListener(type, listener, useCapture)
element.addEventListener(type, listener, options)

事件流回顾

理解第三个参数前,需要了解事件流的三个阶段:

捕获阶段 → 目标阶段 → 冒泡阶段
(从外到内)   (目标元素)   (从内到外)

布尔值形式

// 第三个参数为布尔值,表示是否在捕获阶段触发
// 默认为 false(冒泡阶段触发)

const outer = document.getElementById('outer')
const inner = document.getElementById('inner')

// 冒泡阶段触发(默认)
outer.addEventListener('click', () => {
  console.log('outer - 冒泡')
}, false)

// 捕获阶段触发
outer.addEventListener('click', () => {
  console.log('outer - 捕获')
}, true)

inner.addEventListener('click', () => {
  console.log('inner')
})

// 点击 inner 时输出顺序:
// outer - 捕获
// inner
// outer - 冒泡

配置对象形式

element.addEventListener('click', handler, {
  capture: false,  // 是否在捕获阶段触发,默认 false
  once: false,     // 是否只触发一次,默认 false
  passive: false,  // 是否为被动监听器,默认 false
  signal: null     // AbortSignal,用于移除监听器
})

once - 只触发一次

const btn = document.getElementById('btn')

btn.addEventListener('click', () => {
  console.log('只会触发一次')
}, { once: true })

// 等价于
btn.addEventListener('click', function handler() {
  console.log('只会触发一次')
  btn.removeEventListener('click', handler)
})

passive - 被动监听器

// passive: true 告诉浏览器不会调用 preventDefault()
// 浏览器可以立即滚动,不用等待 JS 执行完毕
// 常用于优化滚动性能

document.addEventListener('touchstart', (e) => {
  // 这里不能调用 e.preventDefault()
  console.log('touch start')
}, { passive: true })

// 滚动事件优化
window.addEventListener('scroll', () => {
  // 处理滚动
}, { passive: true })

signal - 使用 AbortController 移除监听

const controller = new AbortController()

document.addEventListener('click', () => {
  console.log('clicked')
}, { signal: controller.signal })

// 移除监听器
controller.abort()

// 适合批量移除多个监听器
const controller2 = new AbortController()

element.addEventListener('click', handler1, { signal: controller2.signal })
element.addEventListener('keydown', handler2, { signal: controller2.signal })
element.addEventListener('scroll', handler3, { signal: controller2.signal })

// 一次性移除所有监听器
controller2.abort()

完整示例

<!DOCTYPE html>
<html>
<body>
  <div id="outer" style="padding: 50px; background: #eee;">
    outer
    <div id="inner" style="padding: 30px; background: #ddd;">
      inner
    </div>
  </div>

  <script>
    const outer = document.getElementById('outer')
    const inner = document.getElementById('inner')

    // 捕获阶段
    outer.addEventListener('click', () => {
      console.log('1. outer 捕获')
    }, { capture: true })

    // 冒泡阶段
    outer.addEventListener('click', () => {
      console.log('4. outer 冒泡')
    }, { capture: false })

    inner.addEventListener('click', () => {
      console.log('2. inner 捕获')
    }, { capture: true })

    inner.addEventListener('click', () => {
      console.log('3. inner 冒泡')
    }, { capture: false })

    // 点击 inner 输出:
    // 1. outer 捕获
    // 2. inner 捕获
    // 3. inner 冒泡
    // 4. outer 冒泡
  </script>
</body>
</html>

关键点

  • 第三个参数可以是布尔值(是否捕获)或配置对象
  • capture: true 在捕获阶段触发,false(默认)在冒泡阶段触发
  • once: true 让事件只触发一次,自动移除监听器
  • passive: true 提升滚动性能,但不能调用 preventDefault()
  • signal 配合 AbortController 可以方便地批量移除监听器