事件机制、模型
# 事件流
# 事件捕获
事件捕获 是 <strong>由外到内的</strong>

# 事件冒泡
事件捕获 是 <strong>由内到外的</strong>

DOM2 Events 规范规定事件流分为 3 个阶段: <code>事件捕获</code>、<code>到达目标</code> 和 <code>事件冒泡</code> 
# 事件处理
# DOM0 事件处理
- 事件处理函数中的
<code>this</code>指向事件的目标元素 - 一个元素有多个
<code>DOM0</code>事件,则后面的覆盖前面的,最终只执行最后一个<code>DOM0</code>事件
const btn = document.getElementById("zcl");
btn.onclick = function(){
console.log(this) // this 指向btn
console.log('Clicked')
}
// 移除事件
btn.onclick = null;
1
2
3
4
5
6
7
2
3
4
5
6
7
# DOM2 事件处理
通过addEventListener (opens new window)、<code>removeEventListener</code>来添加和移除事件。
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture)
1
2
2
addEventListener我们是比较熟悉了,之前手写事件委托等早已写过。这里注意 第三个参数 可以是一个 option 对象或一个布尔值<code>useCapture</code>。 useCapture 参数指定了该事件处理程序触发的 “时机” :是在事件流的捕获阶段还是冒泡阶段
- true:表示在捕获阶段调用事件处理程序
- false: (默认值)表示在冒泡阶段调用事件处理程序
# 事件对象
- event.preventDefault (opens new window): 阻止事件的默认行为. eg: 选中复选框是点击复选框的默认行为
document.querySelector("#id-checkbox").addEventListener("click", function(event) {
document.getElementById("output-box").innerHTML += "Sorry! <code>preventDefault()</code> won't let you check this!<br>";
event.preventDefault();
}, false)
1
2
3
4
2
3
4
Please click on the checkbox control.
- event.stopPropagation (opens new window): 阻止事件流在 DOM 中的传播,取消后续的事件捕获或冒泡
var div = document.querySelector('#div');
div.addEventListener("click", (e) => {
console.log("clicked");
e.stopPropagation()
}, false);
document.body.addEventListener("click", () => {
console.log("body clicked");
}, false);
// clicked 如果不调用 stopPropagation() 那么点击 div 会有两个 log 记录
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- event.target & event.currentTarget
手写事件委托中,用的是
<code>event.target</code>, 不用event.currentTarget! 看下面就知道为啥了hh
- event.target: 指向触发事件的元素。event.target是事件的真正发出者
- event.current: 指向事件绑定的元素。vent.currentTarget始终是监听事件者
<div id="a">
<div id="b">
<div id="c">
<div id="d"></div>
</div>
</div>
</div>
<script>
document.getElementById('a').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('b').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('c').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('d').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
当我们点击最里层的元素d的时候,会依次输出:
target:d¤tTarget:d
target:d¤tTarget:c
target:d¤tTarget:b
target:d¤tTarget:a
1
2
3
4
2
3
4
# 面试
浏览器的事件模型/机制,关键词:<code>事件捕获</code>、<code>事件冒泡</code>、<code>DOM0 事件</code>、<code>DOM2 事件</code>。理解上还是很容易的,下面来做道题
<div>
<button>123</button>
</div>
btn.addEventListener("click", (e) => {
console.log('btn click capture ')
}, true);
btn.addEventListener("click", (e) => {
console.log('btn click bubble ')
});
body.addEventListener("click", (e) => {
console.log('body click capture')
}, true);
body.addEventListener("click", (e) => {
console.log('body click bubble')
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
问:打印输出啥?
答:body click capture -> btn click capture -> btn click bubble -> body click bubble
参考: 深入理解浏览器的事件机制 (opens new window) DOM事件机制 (opens new window) 【前端词典】滚动穿透问题的解决方案 (opens new window)
编辑 (opens new window)
上次更新: 2025/07/20, 08:30:18