事件机制
DOM 事件模型
每个事件都先经历从上到下(根节点 => 目标节点)的捕获阶段,再经历从下到上(目标节点=>根节点)的冒泡阶段,这决定着元素接收事件的顺序,从而决定函数执行的顺序
事件模型的核心API是
addEventListener(type, fn, true/false)
第三个参数可以选择执行阶段(是否在捕获阶段执行)
- 当给同一个元素绑定 listener 时,仅仅是队列模型,即按照函数定义顺序来执行
- 绑定在不同元素时,当第三个参数为 true 时,就会在捕获阶段来执行函数(爷爷-爸爸-儿子),
可以使用
event.stopPropagation()
来阻止传播(捕获或冒泡)
解释 addEventListener 的第三个参数
当你在使用 addEventListener
方法时,可以选择在事件的捕获阶段或者冒泡阶段监听事件。默认情况下,事件监听器是在冒泡阶段(useCapture 为 false)触发的
在一个 DOM 元素树中,当一个事件发生时,它实际上会经过三个阶段:
- 捕获阶段:事件从根节点向目标节点传播,从外向里。
- 目标阶段:事件到达目标节点,被触发。
- 冒泡阶段:事件从目标节点向根节点传播,从里向外。
Dom
标准事件流的触发的先后顺序为:先捕获再冒泡
下面是一个代码示例,演示如何使用 useCapture
参数:
<div id="outerDiv" style="background-color: yellow; padding: 50px;">
外层 Div
<div id="innerDiv" style="background-color: lightblue; padding: 50px;">
内层 Div
</div>
</div>
<script>
// 获取两个 div 元素
var outerDiv = document.getElementById('outerDiv');
var innerDiv = document.getElementById('innerDiv');
// 捕获阶段的监听器
outerDiv.addEventListener('click', function() {
console.log('外层 Div (捕获)');
}, true); // 注意这里的 true 表示在捕获阶段触发
// 冒泡阶段的监听器
outerDiv.addEventListener('click', function() {
console.log('外层 Div (冒泡)');
}, false); // false 或不设置都表示在冒泡阶段触发
// 捕获阶段的监听器
innerDiv.addEventListener('click', function(event) {
console.log('内层 Div (捕获)');
// 阻止事件继续捕获或冒泡
// event.stopPropagation();
}, true);
// 冒泡阶段的监听器
innerDiv.addEventListener('click', function() {
console.log('内层 Div (冒泡)');
}, false);
</script>
在这个例子中,有两个 div 元素,outerDiv 是 innerDiv 的父元素。我们给每个 div 添加了捕获阶段和冒泡阶段的事件监听器。
如果你点击 "内层 Div",那么会首先触发捕获阶段的监听器(从 outerDiv 到 innerDiv),然后触发 innerDiv 的目标事件,最后是冒泡阶段的监听器(从 innerDiv 到 outerDiv)。
外层 Div (捕获)
内层 Div (捕获)
内层 Div (冒泡)
外层 Div (冒泡)
如果你点击 "外层 Div",则只会看到外层 Div 的事件触发。
在上面的代码中,我们还使用了 event.stopPropagation() 方法。这个方法阻止了事件在捕获或冒泡阶段进一步传播。
在这个例子中,当你点击 "内层 Div" 时,捕获阶段的监听器会被触发,但由于 stopPropagation 的调用,冒泡阶段的监听器将不会触发
外层 Div (捕获)
内层 Div (捕获)
e.target vs e.currentTarget
e.target
谁触发的就是谁e.currentTarget
监听谁就是谁(可能是触发的也可能是委托的祖先元素)
事件委托
事件委托,通俗地来讲,就是把一个元素响应事件的函数委托到另一个父元素或祖先元素上
一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡(因为事件都会冒泡到根元素)机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。