在浏览器中,点击一个按钮时,事件并不是瞬间只发生在按钮上,而是经历一次从顶层到目标、再从目标回顶层的完整传播过程,这就是DOM事件流。理解事件流是掌握JavaScript交互开发的基础。
事件流的三阶段
W3C标准将事件流分为三个阶段:
1. 捕获阶段(Capturing Phase):从window/document向下传播到目标元素的父元素,事件在此“寻找”目标。默认监听器不会在此阶段触发,除非明确指定。
2. 目标阶段(Target Phase):事件到达实际触发的元素,即event.target。
3. 冒泡阶段(Bubbling Phase):从目标元素向上传播回window,这是最常用的阶段,大多数事件监听器默认在此阶段触发。
代码实战:控制捕获与冒泡
使用addEventListener的第三个参数useCapture控制监听器在哪个阶段触发:true为捕获阶段,false(默认)为冒泡阶段。
示例HTML结构:- <div id="grandparent">
- 爷爷
- <div id="parent">
- 爸爸
- <div id="child">儿子</div>
- </div>
- </div>
复制代码
注册监听器:- const grandparent = document.getElementById('grandparent');
- const parent = document.getElementById('parent');
- const child = document.getElementById('child');
- // 捕获阶段
- grandparent.addEventListener('click', () => console.log('Grandparent - Capture'), true);
- parent.addEventListener('click', () => console.log('Parent - Capture'), true);
- // 冒泡阶段
- parent.addEventListener('click', () => console.log('Parent - Bubble'));
- child.addEventListener('click', () => console.log('Child - Bubble'));
- // 点击child后控制台输出:
- // Grandparent - Capture
- // Parent - Capture
- // Child - Bubble
- // Parent - Bubble
复制代码
点击child时,捕获阶段先执行grandparent和parent的捕获监听器,然后到达目标child(冒泡监听器执行),最后向上冒泡到parent(冒泡监听器执行)。
阻止事件传播
使用event.stopPropagation()可阻止事件在DOM树中继续传播(同时阻止后续捕获和冒泡):- child.addEventListener('click', function(event) {
- event.stopPropagation();
- console.log('Child clicked, stop!');
- });
复制代码
使用event.stopImmediatePropagation()则不仅阻止传播,还阻止当前元素上其他相同事件的监听器执行:- child.addEventListener('click', function(event) {
- event.stopImmediatePropagation();
- console.log('Handler 1');
- });
- child.addEventListener('click', function() {
- console.log('Handler 2'); // 不会执行
- });
复制代码
注意:在原生JavaScript中,return false不会阻止事件传播,必须显式调用stopPropagation或stopImmediatePropagation。
应用场景
1. 事件委托:利用冒泡,在父元素上统一监听子元素事件,提升性能。- ul.addEventListener('click', function(event) {
- if (event.target.tagName === 'LI') {
- console.log('Clicked:', event.target.innerText);
- }
- });
复制代码
2. 模态框关闭逻辑:点击遮罩层关闭弹窗,点击弹窗内部不关闭。- overlay.addEventListener('click', () => closeModal());
- modal.addEventListener('click', (event) => event.stopPropagation());
复制代码
3. 高级拦截:在捕获阶段提前终止事件,实现全局权限检查或日志记录。- document.addEventListener('click', function(event) {
- if (event.target.disabled) {
- event.stopPropagation();
- event.preventDefault();
- }
- }, true);
复制代码
掌握事件流三个阶段:先捕获,后目标,再冒泡;利用冒泡实现事件委托;使用stopPropagation控制事件传播范围。这是JS交互开发的必备技能。 |