一、核心概念:DOM事件流(事件传播机制)
DOM事件模型描述了事件在DOM树中传播的完整流程,分为三个阶段,遵循“先捕获后冒泡”的顺序:
-
事件捕获阶段(Capture Phase)
- 事件从最顶层的Window对象开始,逐级向下传播到目标元素的父元素(不包含目标元素本身)。
- 作用:提前拦截事件(如阻止某些操作)。
-
目标阶段(Target Phase)
- 事件到达触发事件的目标元素(如点击的按钮),执行该元素上的事件处理函数。
-
事件冒泡阶段(Bubbling Phase)
- 事件从目标元素开始,逐级向上传播到最顶层的Window对象。
- 作用:实现事件委托(通过父元素处理子元素事件)。
二、关键机制:捕获与冒泡的触发与控制
1. 如何触发捕获/冒泡阶段的事件?
通过addEventListener的第三个参数控制(useCapture,默认为false):
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// 冒泡阶段触发(默认):事件冒泡到parent时执行
parent.addEventListener('click', () => console.log('parent冒泡'), false);
// 捕获阶段触发:事件捕获到parent时执行
parent.addEventListener('click', () => console.log('parent捕获'), true);
// 点击child时,执行顺序:
// 1. parent捕获(捕获阶段)
// 2. child点击(目标阶段)
// 3. parent冒泡(冒泡阶段)
2. 如何阻止事件传播?
-
event.stopPropagation():阻止事件继续传播(无论当前在捕获还是冒泡阶段)。child.addEventListener('click', (e) => { e.stopPropagation(); // 阻止事件传播到父元素 console.log('child被点击'); }); -
event.stopImmediatePropagation():不仅阻止传播,还会阻止当前元素上后续的同类型事件处理函数。child.addEventListener('click', (e) => { e.stopImmediatePropagation(); console.log('第一个处理函数'); }); child.addEventListener('click', () => { console.log('第二个处理函数(不会执行)'); });
三、核心应用:事件委托(Event Delegation)
1. 原理
利用事件冒泡机制,将子元素的事件处理函数绑定到父元素上,通过event.target判断具体触发事件的子元素。
2. 代码示例(动态列表的点击事件)
<ul id="list">
<li>项目1</li>
<li>项目2</li>
<!-- 动态添加的li也能触发事件 -->
</ul>
<script>
const list = document.getElementById('list');
// 事件委托:绑定到父元素ul
list.addEventListener('click', (e) => {
// 判断触发事件的是li元素
if (e.target.tagName === 'LI') {
console.log('点击了:', e.target.textContent);
}
});
// 动态添加子元素(无需重新绑定事件)
const newLi = document.createElement('li');
newLi.textContent = '项目3';
list.appendChild(newLi);
</script>
3. 优势
- 减少事件绑定次数(尤其对大量子元素,如列表),优化性能;
- 支持动态添加的元素(无需重新绑定事件)。
四、问题
-
问:事件捕获和冒泡的执行顺序是什么?
- 答:先捕获(从Window到目标父元素)→ 目标阶段(目标元素)→ 再冒泡(从目标元素到Window)。
-
问:事件委托的核心原理是什么?适用于什么场景?
- 答:核心是利用事件冒泡,将子元素事件绑定到父元素,通过
event.target定位触发元素。适用于动态生成的元素(如列表项、表格行),或大量子元素的场景(减少内存占用)。
- 答:核心是利用事件冒泡,将子元素事件绑定到父元素,通过
-
问:如何阻止默认事件(如表单提交、链接跳转)?
- 答:使用
event.preventDefault(),例如:// 阻止链接跳转 document.querySelector('a').addEventListener('click', (e) => { e.preventDefault(); // 不会跳转到href指定的地址 });
- 答:使用
-
问:
onclick和addEventListener的区别是什么?- 答:
onclick只能绑定一个事件处理函数(后绑定的会覆盖前一个),且只能在冒泡阶段触发;addEventListener可绑定多个函数,支持捕获阶段触发,功能更灵活。
- 答:
五、总结
“DOM事件模型的核心是‘捕获→目标→冒泡’的三阶段传播机制:
- 捕获阶段从顶层到目标父元素,可用于提前拦截事件;
- 冒泡阶段从目标到顶层,是事件委托的基础(通过父元素处理子元素事件);
- 实际开发中,常用事件委托优化大量子元素的事件处理,用
stopPropagation控制传播,用preventDefault阻止默认行为。
例如在电商商品列表中,通过事件委托给ul绑定点击事件,无需为每个li单独绑定,既节省内存,又支持动态添加商品项的交互。”