理解JS事件冒泡
<style>.artBox { margin: auto; max-width: 1200px; font: normal 20px/1.5 Simsun, NSimsun, sans-serif;position: relative; }
.artBox p { margin: 12px 0; }
.artBox > blockquote { margin: 12px 2em; padding: 8px 8px 8px 16px; border-left: 4px solid gray; background: beige; }
.artBox mark { padding: 2px 8px; background: aliceblue; }
</style>
<div class="artBox">
<p>当 html 元素彼此间存在“血亲关系”,即它们层层嵌套,那么,最里层的元素发起人机交互动作时,外层的任意一层都可以接收到这个事件。可以将层层嵌套的元素想象成俄罗斯套娃:你戳一下最里层的娃娃,它外面的每一个娃娃都可以依次感知到被戳了一下。这就是所谓的“冒泡”:事件被触发后从触发点外溢,这个事件就像涟漪一样被传输到存在“直系亲属”关系的所有元素,只要它们原意监听。</p>
<blockquote>
<p><strong>网页“直系关系”示意图谱:</strong></p>
<p>window → document → html → body → div(或其他元素)</p>
</blockquote>
<p>试看下面的代码,重点理解 html 代码:四个 div 盒子层层嵌套,它们是“四代同堂”的直系亲属关系——</p>
<div class="codebox" data-prev="1">
<style>
.ttDiv { margin: auto; width: 40vw; aspect-ratio: 1 / 1; border: 1px solid gray; display: grid; place-items: center; position: relative; }
.ttDiv::after { content: attr(data-counter); position: absolute; left: 10px; top: 5px; }
.outer1 { width: 40vw; margin-top: 100px; }
.inner1 { width: 30vw; }
.inner2 { width: 20vw; }
.inner3 { width: 10vw; }
</style>
<div id="div1" class="ttDiv outer1" data-counter="0">
<div id="div2" class="ttDiv inner1" data-counter="0">
<div id="div3" class="ttDiv inner2" data-counter="0">
<div id="div4" class="ttDiv inner3" data-counter="0"></div>
</div>
</div>
</div>
<script>
// 点击计数器函数
const clicking = (element) => {
let counter = element.dataset.counter * 1 + 1;
element.dataset.counter = counter;
}
// 获取所有的 .ttDiv 盒子
const ttDivs = document.querySelectorAll('.ttDiv');
// 盒子被点击时触发计数
ttDivs.forEach(ttdiv => {
ttdiv.onclick = () => clicking(ttdiv);
});
</script>
</div>
<p>这组代码为每一个 div 设置了<mark>::after</mark>伪元素,用以展示被点击的次数。JS 代码设计了一个计数器函数:元素如果被点击则计数器加 1,然后每一个 div 都监听点击事件,该事件如果被触发则执行计数器函数。</p>
<p>理解冒泡事件,可以点击代码栏右上角“预览”按钮进入预览页面,点击不同层级的 div 元素,观察计数器的变化。</p>
<p>如你所见:冒泡是从里往外发起。如果点击的不是最里层的,那么,不被点击的里层 div 计数器不会发生变化。</p>
<blockquote>
<p><strong>网页元素冒泡路径示意图:</strong></p>
<p>divs(或其它元素)→ body → html → document → window</p>
</blockquote>
<p>冒泡如果不人为阻止,它一直会往外冒,直至“直系亲属”的最后一个成员。换言之,上述“四代同堂”的 div 之外,如果还有其它的属于它们祖先的 div 或是其它容器盒子,都会接收到冒泡事件,直至DOM“族谱”的根(window对象)才会停止。</p>
<p>阻止冒泡指特定元素的事件私有化,不允许外层的“直系亲属”捕获相关事件。这时候,可以在特定元素的指定事件(例如点击事件)中加入 <mark>event.stopPropagation()</mark>(中断传播)加以实现。前面的代码示例,每一个 div 都有 id 标识符,JS 中的元素点击事件代码可以通过识别目标的 id,如果与指定点击事件要阻止冒泡的 div 的 id 相匹配就阻隔事件的冒泡行为。可以这样改进 div 的点击事件:</p>
<div class="codebox">
// 盒子被点击时触发计数,阻止 div4 的点击行为冒泡
ttDivs.forEach( ttdiv => {
ttdiv.onclick = (event) => {
if (event.target.id === 'div4') event.stopPropagation();
clicking(ttdiv);
}
});
</div>
<p>这么一来,最里层的 div 被点击时,只有它自己的计数器发生变化,外层所有元素都不再能够捕获到点击事件。而其长辈 div 没有受到限制,它们的点击事件仍然会冒泡。</p>
</div>
<script type="module">
import linenumber from 'https://638183.freep.cn/638183/web/helight/linenumber.js';
linenumber();
</script> 本帖最后由 杨帆 于 2026-3-10 15:06 编辑
理解JS事件冒泡很重要,但理解起来很抽象,感谢马老师深入浅出、言简意赅、通俗易懂的讲授{:4_180:} 看了预览,这些事件涟漪还真是一层层传递的呢。
有趣的冒泡事件,而且它还能被阻隔。
感谢黑黑的讲解,学习了{:4_187:}
杨帆 发表于 2026-3-10 15:05
理解JS事件冒泡很重要,但理解起来很抽象,感谢马老师深入浅出、言简意赅、通俗易懂的讲授
{:4_190:} 红影 发表于 2026-3-10 22:12
看了预览,这些事件涟漪还真是一层层传递的呢。
有趣的冒泡事件,而且它还能被阻隔。
感谢黑黑的讲解,学 ...
JS围绕HTML进行设计,非常不简单
页:
[1]