-----------jQuery 语法------------
需要说明:jQuery 的事件系统默认只支持冒泡阶段 ,不支持捕获阶段(addEventListener 的 true 参数在 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.target或closest()判断实际点击的元素,适合处理动态添加的子元素,减少内存占用。
案例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,只是条件执行关闭逻辑)。注意最后需移除监听器避免浪费。
总结
| 阶段 | 传播方向 | 典型用途 |
|---|---|---|
| 捕获 | 从根节点到目标节点 | 全局预处理、事件拦截、记录埋点 |
| 目标 | 事件到达目标元素 | 执行具体业务逻辑 |
| 冒泡 | 从目标节点回到根节点 | 事件委托、父级统一处理子元素事件 |
掌握捕获与冒泡,可以写出更灵活、高效的事件处理代码。