IT俱乐部 JavaScript JavaScript 事件流:从”捕获”到”冒泡”的完整旅程

JavaScript 事件流:从”捕获”到”冒泡”的完整旅程

当你点击页面上的一个按钮时,这个点击动作并不是瞬间只在按钮上发生的。
在浏览器的世界里,这个点击事件像一阵风,经历了一场从上到下,再从下到上的完整旅行。

这就是 DOM 事件流(Event Flow)

1. 🤔 什么是事件流?

定义
事件流描述的是从页面中接收事件的顺序。

在早期的浏览器大战中, Netscape(网景)和 IE 提出了两种完全相反的事件传播模型:

  • Netscape:主张事件捕获(Event Capturing),即事件从最外层向内层传播。
  • IE:主张事件冒泡(Event Bubbling),即事件从最内层向外层传播。

最终,W3C 制定了标准,将两者结合:先捕获,后冒泡

通俗比喻
想象你在一个多层办公楼里扔了一个石子(触发事件)。

  1. 捕获阶段:石子从楼顶(window)一层层往下掉,经过每一层的地板。
  2. 目标阶段:石子落在了某一层的具体房间(目标元素)里。
  3. 冒泡阶段:石子落地后产生的回声或震动,从那个房间一层层往上传播回楼顶。

2. 🎬 事件流的三个阶段

当一个事件发生时,它会依次经历以下三个阶段:

第一阶段:捕获阶段(Capturing Phase)

  • 方向:从根节点(window / document)向下传播,直到目标元素的父元素。
  • 特点:此时事件正在“寻找”目标。
  • 注意:默认情况下,我们注册的事件监听器不会在这个阶段触发(除非特别指定)。

第二阶段:目标阶段(Target Phase)

  • 方向:事件到达实际触发事件的元素(event.target)。
  • 特点:这是事件真正发生的地方。

第三阶段:冒泡阶段(Bubbling Phase)

  • 方向:从目标元素向上传播,经过父元素、祖父元素……直到根节点。
  • 特点:这是最常用的阶段。大多数事件监听器默认都注册在这个阶段。
      Window
        |
      Document
        |
       HTML
        |
       Body          

3. 💻 代码实战:如何控制捕获与冒泡?

在 JavaScript 中,我们使用 addEventListener 来注册事件。它的第三个参数决定了监听器在哪个阶段触发。

语法

element.addEventListener(event, handler, useCapture);
  • useCapturetrue:在捕获阶段触发。
  • useCapturefalse(默认):在冒泡阶段触发。

🧪 实验代码

假设我们有如下 HTML 结构:

爷爷
爸爸
儿子

我们给每一层都绑定点击事件,分别测试捕获和冒泡:

const grandparent = document.getElementById("grandparent");
const parent = document.getElementById("parent");
const child = document.getElementById("child");
function log(msg, phase) {
  console.log(`${msg} - ${phase}`);
}
// 1. 注册捕获阶段监听器 (useCapture = true)
grandparent.addEventListener(
  "click",
  () => log("Grandparent", "Capture"),
  true,
);
parent.addEventListener("click", () => log("Parent", "Capture"), true);
// 2. 注册冒泡阶段监听器 (useCapture = false, 默认)
parent.addEventListener("click", () => log("Parent", "Bubble"));
child.addEventListener("click", () => log("Child", "Bubble"));
// 3. 点击 "儿子" (child)

📊 输出结果

当你点击

时,控制台输出顺序如下:

  1. Grandparent - Capture (捕获:从外向内)
  2. Parent - Capture (捕获:从外向内)
  3. Child - Bubble (目标:通常视为冒泡的一部分,或者单独的目标阶段)
  4. Parent - Bubble (冒泡:从内向外)

注意grandparent 没有注册冒泡监听器,所以最后没有输出 Grandparent - Bubble

4. 🛑 如何阻止事件传播?

有时候,我们不希望事件继续传播(例如:点击弹窗内部不希望关闭弹窗,但点击背景要关闭)。

方法一:event.stopPropagation()

作用:停止事件在 DOM 树中的进一步传播(既阻止后续的捕获,也阻止后续的冒泡)。

child.addEventListener("click", function (event) {
  event.stopPropagation();
  console.log("Child clicked, stop!");
});

结果:点击 child 后,只会执行 child 的回调,parentgrandparent 的回调都不会执行。

方法二:event.stopImmediatePropagation()

作用:不仅阻止事件传播,还阻止当前元素上其他相同事件的监听器执行。

child.addEventListener("click", function (event) {
  event.stopImmediatePropagation();
  console.log("Handler 1");
});
child.addEventListener("click", function () {
  console.log("Handler 2"); // 这行永远不会执行
});

❌ 误区:return false

在 jQuery 中,return false 等价于同时调用 stopPropagation()preventDefault()
但在原生 JS 中,return false 无效,必须显式调用上述方法。

5. 🚀 实际应用场景:为什么我们需要它们?

场景一:事件委托(利用冒泡)

这是冒泡最著名的应用。与其给 1000 个列表项分别绑定事件,不如给父元素

    绑定一个事件,利用冒泡机制统一处理。

    // 利用冒泡,只在 ul 上监听
    ul.addEventListener("click", function (event) {
      if (event.target.tagName === "LI") {
        console.log("Clicked:", event.target.innerText);
      }
    });

    场景二:模态框关闭逻辑(混合使用)

    常见需求:点击遮罩层关闭弹窗,点击弹窗内容不关闭。

    const overlay = document.getElementById("overlay");
    const modal = document.getElementById("modal");
    // 1. 点击遮罩层,关闭弹窗(冒泡阶段)
    overlay.addEventListener("click", () => {
      console.log("Close Modal");
    });
    // 2. 点击弹窗内部,阻止冒泡,防止触发 overlay 的点击事件
    modal.addEventListener("click", (event) => {
      event.stopPropagation();
      console.log("Modal Content Clicked");
    });

    场景三:高级拦截(利用捕获)

    如果你想在一个事件到达目标之前“拦截”它(例如全局权限检查、日志记录),可以使用捕获阶段。因为捕获发生在冒泡之前,你可以提前终止事件。

    // 在捕获阶段拦截所有点击
    document.addEventListener(
      "click",
      (event) => {
        if (event.target.disabled) {
          event.stopPropagation(); // 禁止点击禁用元素
          event.preventDefault();
        }
      },
      true,
    ); // 注意这里是 true
    

    💡 总结

    特性 事件捕获 (Capturing) 事件冒泡 (Bubbling)
    传播方向 从上到下 (Window -> Target) 从下到上 (Target -> Window)
    触发时机 早期,用于拦截 晚期,用于处理
    默认行为 需设置 useCapture: true 默认 (useCapture: false)
    主要用途 事件拦截、全局监听 事件委托、常规交互

    🚀 博主寄语
    事件流就像水的流动,既有从天而降的雨(捕获),也有汇聚成河向上的蒸汽(冒泡)。

    记住三个关键点

    1. 顺序:先捕获,后目标,再冒泡。
    2. 委托:利用冒泡实现高性能的事件代理。
    3. 阻止:使用 stopPropagation() 控制事件传播范围。

    理解了事件流,你就掌握了 DOM 交互的脉搏!

    希望这篇文档能帮你彻底搞懂事件捕获与冒泡!

    到此这篇关于JavaScript 事件流:从“捕获”到“冒泡”的完整旅程的文章就介绍到这了,更多相关js事件流从捕获到冒泡内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!

    相关文章

    • javascript简单进制转换实现方法

      这篇文章主要介绍了javascript简单进制转换实现方法,涉及javascript字符串转换与数值操作相关技巧,需要的朋友可以参考下

      2016-11-11

    • JavaScript对象参数的引用传递

      这篇文章主要为大家介绍了JavaScript对象参数的引用传递的相关资料,感兴趣的小伙伴们可以参考一下

      2016-01-01

    • 微信小程序开发自定义tabBar实战案例(定制消息99+小红心)

      一定的需求情况下无法使用小程序原生的tabbar的时候,需要自行实现一个和tabbar功能一模一样的自制组件,下面这篇文章主要给大家介绍了关于微信小程序开发自定义tabBar(定制消息99+小红心)的相关资料,需要的朋友可以参考下

      2022-12-12

    • BootStrap的JS插件之轮播效果案例详解

      Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架。这篇文章主要介绍了BootStrap的JS插件之轮播效果案例详解的相关资料,需要的朋友可以参考下

      2016-05-05

    • 在uni-app中自定义输入框placeholder的样式的问题示例展示

      这篇文章主要介绍了在uni-app中自定义输入框placeholder的样式的问题示例展示,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧

      2025-05-05

    • 前端监控页面异常的常用方法

      前端开发常见问题之一: 资源异常、js异常,页面出了问题常见的想法是:如何看到错误信息,资源脚本是否正常,像js、css、图片这些资源文件经常受网络等原因,导致资源加载异常,这些会直接影响我们的页面,本文给大家介绍前端监控页面异常的常用方法,需要的朋友可以参考下

      2025-04-04

    • 微信小程序实现点击按钮修改文字大小功能【附demo源码下载】

      这篇文章主要介绍了微信小程序实现点击按钮修改文字大小功能,涉及微信小程序事件绑定及setData动态修改Page页面data数据,进而控制页面元素属性动态改变的相关操作技巧,需要的朋友可以参考下

      2017-12-12

    • JS解决url传值出现中文乱码的另类办法

      为什么用表单的方式就可以传递中文,而URL的方式就不行了呢?非得用URL传值的方式才能解决问题吗?这里我想到了动态表单,何不用它来解决呢

      2013-04-04

    • es6 for循环中let和var区别详解

      这篇文章主要介绍了es6 for循环中let和var区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

      2020-01-01

    • JAVASCRIPT style 中visibility和display之间的区别

      visibility属性用来确定元素是显示还是隐藏的,这用visibility="visible|hidden"来表示(visible表示显示,hidden表示隐藏)。

      2010-01-01

    最新评论

    本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/navsub/js/17740.html
    下一篇
    联系我们

    联系我们

    在线咨询: QQ交谈

    邮箱: 1120393934@qq.com

    工作时间:周一至周五,9:00-17:30,节假日休息

    关注微信
    微信扫一扫关注我们

    微信扫一扫关注我们

    返回顶部