Javascript提高:冒泡和捕获的典型案例-由Deepseek产生

-----------jQuery 语法------------

需要说明:jQuery 的事件系统默认只支持冒泡阶段 ,不支持捕获阶段(addEventListenertrue 参数在 jQuery 中没有直接对应)。因此,主要演示冒泡阶段的阻止传播、事件委托等经典场景。

1. 展示冒泡顺序(嵌套元素)

点击最内层元素,观察事件从目标元素向上冒泡到外层的过程。

html 复制代码
<div id="outer" style="padding: 40px; background: lightblue;">
  <div id="middle" style="padding: 30px; background: lightgreen;">
    <div id="inner" style="padding: 20px; background: lightcoral;">点我</div>
  </div>
</div>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  // jQuery 默认在冒泡阶段触发
  $('#outer').on('click', function() { console.log('冒泡阶段: outer'); });
  $('#middle').on('click', function() { console.log('冒泡阶段: middle'); });
  $('#inner').on('click', function() { console.log('冒泡阶段: inner'); });
</script>

输出顺序 (点击 inner):

复制代码
冒泡阶段: inner
冒泡阶段: middle
冒泡阶段: outer

2. 阻止事件冒泡(模态框关闭按钮)

点击关闭按钮时,不希望触发背景的关闭逻辑。

html 复制代码
<div id="modal" style="position: fixed; background: rgba(0,0,0,0.5); width: 100%; height: 100%;">
  <div id="modalContent" style="background: white; margin: 20% auto; width: 200px; padding: 20px;">
    <button id="closeBtn">关闭</button>
  </div>
</div>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  // 点击背景关闭模态框
  $('#modal').on('click', function() {
    console.log('关闭模态框');
    $(this).hide();
  });

  // 点击关闭按钮,阻止冒泡
  $('#closeBtn').on('click', function(e) {
    e.stopPropagation();   // 阻止事件向上冒泡
    console.log('按钮点击,关闭模态框,不会触发背景的关闭');
    $('#modal').hide();
  });
</script>

关键e.stopPropagation() 在 jQuery 事件中同样有效,可以阻止冒泡到父元素。


3. 事件委托(处理动态添加的子元素)

为动态生成的列表项绑定删除功能,利用冒泡委托给父元素处理。

html 复制代码
<ul id="todoList">
  <li>任务1 <button class="del">删除</button></li>
  <li>任务2 <button class="del">删除</button></li>
</ul>
<button id="addBtn">添加新任务</button>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  // 事件委托:监听 ul 上的点击,但只处理 .del 按钮的点击(利用冒泡)
  $('#todoList').on('click', '.del', function(e) {
    $(this).closest('li').remove();   // 删除所在的 li
    console.log('删除任务');
  });

  // 动态添加新任务,无需为新的 .del 单独绑定事件
  $('#addBtn').on('click', function() {
    const newLi = $('<li>新任务 <button class="del">删除</button></li>');
    $('#todoList').append(newLi);
  });
</script>

语法.on(events, selector, handler) 中传入 selector 参数即实现事件委托,底层利用冒泡机制。


补充说明:jQuery 与事件捕获

jQuery 没有提供 注册捕获阶段监听器的 API。如果确实需要在捕获阶段处理事件(例如案例4的"点击外部关闭浮层"),必须混合原生 JavaScript 的 addEventListener,但这样就不再是纯 jQuery 语法了。通常在实际项目中,可以利用 jQuery 的事件委托 + 判断目标元素来模拟类似效果,或者直接使用原生方法。

若坚持纯 jQuery,以下是一种变通写法(不使用捕获,而是监听 document 的冒泡阶段并判断目标):

javascript 复制代码
$(document).on('click', function(e) {
  if (!$(e.target).closest('#adPopup').length) {
    $('#adPopup').hide();
  }
});

这种方式也能实现"点击浮层外部关闭",但依赖冒泡阶段,且无法阻止事件在到达目标元素之前被处理(若内部有其他冒泡阻断逻辑则可能失效)。好在绝大多数场景下冒泡阶段足够使用。


总结

  • 冒泡阶段 :jQuery 完美支持,推荐使用 .on()
  • 阻止冒泡 :调用 e.stopPropagation()
  • 事件委托.on(events, selector, handler) 是标准用法。
  • 捕获阶段 :jQuery 不支持,需要时可混合原生代码。
    以下是事件冒泡(Bubbling)与事件捕获(Capturing,也称"截获")的典型应用案例。所有示例均使用 addEventListener 的标准形式,第三个参数为 true 时注册在捕获阶段,为 false 或不传时注册在冒泡阶段。

---------------非JQuery语法--------------

案例1:展示事件传播顺序(嵌套元素)

场景 :三个嵌套 <div>,分别添加捕获阶段和冒泡阶段的监听器,点击最内层元素,观察控制台输出顺序。

html 复制代码
<div id="outer" style="padding: 40px; background: lightblue;">
  <div id="middle" style="padding: 30px; background: lightgreen;">
    <div id="inner" style="padding: 20px; background: lightcoral;">点我</div>
  </div>
</div>

<script>
  function log(phase, id) {
    console.log(`${phase}阶段: ${id}`);
  }

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

  // 捕获阶段监听(第三个参数 true)
  outer.addEventListener('click', () => log('捕获', 'outer'), true);
  middle.addEventListener('click', () => log('捕获', 'middle'), true);
  inner.addEventListener('click', () => log('捕获', 'inner'), true);

  // 冒泡阶段监听(第三个参数 false 或不传)
  outer.addEventListener('click', () => log('冒泡', 'outer'), false);
  middle.addEventListener('click', () => log('冒泡', 'middle'), false);
  inner.addEventListener('click', () => log('冒泡', 'inner'), false);
</script>

输出顺序 (点击 inner 时):

复制代码
捕获阶段: outer
捕获阶段: middle
捕获阶段: inner
冒泡阶段: inner
冒泡阶段: middle
冒泡阶段: outer

关键点 :事件从 window 向下捕获到目标元素,再从目标元素向上冒泡到 window。目标元素的捕获与冒泡监听器都会执行,但按注册顺序(通常先捕获后冒泡)。


案例2:阻止事件冒泡(避免父级响应)

场景:模态框中的关闭按钮点击时,只希望关闭模态框,不希望触发模态框背景的点击事件。

html 复制代码
<div id="modal" style="position: fixed; background: rgba(0,0,0,0.5); width: 100%; height: 100%;">
  <div id="modalContent" style="background: white; margin: 20% auto; width: 200px; padding: 20px;">
    <button id="closeBtn">关闭</button>
  </div>
</div>

<script>
  const modal = document.getElementById('modal');
  const closeBtn = document.getElementById('closeBtn');

  // 点击背景关闭模态框(冒泡阶段)
  modal.addEventListener('click', () => {
    console.log('关闭模态框');
    modal.style.display = 'none';
  });

  // 点击关闭按钮,阻止冒泡,避免同时触发背景的关闭逻辑
  closeBtn.addEventListener('click', (e) => {
    e.stopPropagation();   // 阻止事件向上冒泡
    console.log('按钮点击,关闭模态框,但不会重复触发背景');
    modal.style.display = 'none';
  });
</script>

关键点stopPropagation() 可以阻止事件继续传播(既阻断捕获也阻断冒泡,但同一元素上的其他同级监听器仍会执行)。

扩展 :如需阻止同一元素上其他监听器的执行,使用 stopImmediatePropagation()


案例3:事件委托(利用冒泡处理动态子元素)

场景 :一个待办列表,可动态添加新的 li 元素,所有 li 的点击删除功能通过父级监听实现。

html 复制代码
<ul id="todoList">
  <li>任务1 <button class="del">删除</button></li>
  <li>任务2 <button class="del">删除</button></li>
</ul>
<button id="addBtn">添加新任务</button>

<script>
  const todoList = document.getElementById('todoList');
  const addBtn = document.getElementById('addBtn');

  // 事件委托:监听 ul 上的点击(冒泡阶段)
  todoList.addEventListener('click', (e) => {
    // 检查点击的目标或冒泡路径中是否有 .del 按钮
    const delBtn = e.target.closest('.del');
    if (delBtn) {
      const li = delBtn.closest('li');
      if (li) li.remove();
      console.log('删除任务');
    }
  });

  // 动态添加新任务(无需为新按钮单独绑定监听)
  addBtn.addEventListener('click', () => {
    const newLi = document.createElement('li');
    newLi.innerHTML = `新任务 <button class="del">删除</button>`;
    todoList.appendChild(newLi);
  });
</script>

关键点 :利用冒泡机制,将监听器绑定在稳定的父元素上,通过 event.targetclosest() 判断实际点击的元素,适合处理动态添加的子元素,减少内存占用。


案例4:在捕获阶段提前拦截(阻止非目标元素的特定事件)

场景:页面中有一个广告浮层,点击浮层以外的任意区域时应关闭浮层,但浮层内部点击不应关闭。利用捕获阶段监听整个页面,判断点击目标是否在浮层内。

html 复制代码
<div id="adPopup" style="width: 200px; height: 150px; background: yellow; position: fixed; top: 50px; left: 50px;">
  我是广告
  <button id="adInnerBtn">广告内按钮</button>
</div>
<button id="showAd">显示广告</button>

<script>
  const adPopup = document.getElementById('adPopup');
  const showAd = document.getElementById('showAd');

  function showAdPopup() {
    adPopup.style.display = 'block';
    // 在 document 的捕获阶段监听一次点击,用于关闭浮层
    document.addEventListener('click', closeAdOnOutsideClick, true);
  }

  function closeAdOnOutsideClick(e) {
    // 如果点击的目标不在广告浮层内,则关闭并移除监听
    if (!adPopup.contains(e.target)) {
      adPopup.style.display = 'none';
      document.removeEventListener('click', closeAdOnOutsideClick, true);
      console.log('点击外部,关闭广告');
    } else {
      console.log('点击广告内部,不关闭');
    }
  }

  showAd.addEventListener('click', showAdPopup);
  // 初始隐藏
  adPopup.style.display = 'none';
</script>

关键点 :捕获阶段在事件到达目标之前就被触发,可以优先判断并决定是否后续处理(此处并未使用 stopPropagation,只是条件执行关闭逻辑)。注意最后需移除监听器避免浪费。


总结

阶段 传播方向 典型用途
捕获 从根节点到目标节点 全局预处理、事件拦截、记录埋点
目标 事件到达目标元素 执行具体业务逻辑
冒泡 从目标节点回到根节点 事件委托、父级统一处理子元素事件

掌握捕获与冒泡,可以写出更灵活、高效的事件处理代码。

相关推荐
蒟蒻星球住民1 小时前
web应用技术作业01
前端·javascript·firefox
Csvn1 小时前
前端团队协作
前端
道友可好1 小时前
Superpowers:给 AI 编程助手装上超能力
前端·人工智能·后端
协享科技1 小时前
Vue 3 实现抖音式卡片滑动交互:从零到完整方案
前端·vue.js·交互·ai编程·英语·自考英语
_xaboy2 小时前
开源Vue组件FormCreate通过 JSON 生成TinyVue表单
前端·vue.js·低代码·开源·json·表单设计器
ZC跨境爬虫2 小时前
跟着 MDN 学CSS day_44:响应式设计——让网页适配所有屏幕的完整指南
前端·css·ui·html·tensorflow
前端不太难2 小时前
Edge AI 时代:从数据中心到终端,算力如何无处不在?
前端·人工智能·edge
Highcharts.js2 小时前
Highcharts v13 全新时间轴标签边界格式|让时间维度表达更智能
前端·信息可视化·时间序列·图表开发·chart·自定义标签·可视化开发
lichenyang4532 小时前
鸿蒙研读 10:@Provider/@Consumer、RelativeContainer、onNewWant
前端