事件在dom树的传递分为两种
第一种: 事件捕获 从上而下
即一个事件发生之后,会从根一直传递到目标元素,也就是window -> document -> html -> body -> ... -> target 的传递方向。
第二种:事件冒泡 从下到上
与事件捕获相反,一个事件发生后会从目标元素一直传递到根元素,也就是target -> parent -> ... -> body -> html -> document -> window 的传递方向。
W3C制定的标准是 事件捕获+事件冒泡
即一个事件发生之后,会从根一直传递到目标元素,也就是window -> document -> html -> body -> ... -> target -> parent -> ... -> body -> html -> document -> window 的传递方向,形成了一个循环
传递方向如下图
事件监听
<html>
<head></head>
<body>
<div id="爷爷">
<div id="父亲">
<div id="儿子">C</div>
</div>
</div>
</body>
</html>
<script>
document.getElementById('爷爷').addEventListener('click', (e) => {
console.log('clicked 爷爷')
})
document.getElementById('父亲').addEventListener('click', (e) => {
console.log('clicked 父亲')
})
document.getElementById('儿子').addEventListener('click', (e) => {
console.log('clicked 儿子');
});
</script>
当我们点击儿子节点,触发事件的顺序按道理应该是爷爷->父亲->儿子->父亲->爷爷
但是,实际触发的流程是儿子->父亲->爷爷,
为什么呢?问题就出在addEventListener它的第三个参数上,
target.addEventListener(type, listener, useCapture);
当useCapture=true时,事件监听器将在捕获阶段被触发,而useCapture=false时,事件监听器将在冒泡阶段触发。useCapture默认为false。
事件拦截
- stopPropagation 阻止事件传递过程
{
document.getElementById('儿子').addEventListener('click', (e) => {
console.log('clicked 儿子')
e.stopPropagation(); // 将阻止事件的冒泡,及父亲和爷爷的点击事件不会触发
})
}
- preventDefault 阻止默认行为
如<a href="">+click会跳转,那perventDefault则会阻止跳转这一行为,及点击后不会跳转
如<input> + 键盘敲击会有输入,那perventDefault则会阻止输入这一行为,及敲击键盘后input组件没有输入内容