假设有三个形成父子关系的元素:
并且为这个三个元素都添加了onclick事件处理函数。如果用户点击中间的蓝色方块,则会在珊瑚色方块和绿色方块中都触发点击事件。但是哪个事件会先触发呢?换句话说,事件的处理顺序是什么?
两种模型:
在过去,Netscape和Microsoft得出了不同的结论:
Netscape认为事件应该由外向内传播,即祖先元素上的事件先发生。Microsoft则认为事件应该由内向外传播,即目标元素上的事件优先。
这两种模型的事件顺序完全相反。
W3C模型
W3C综合两个模型的方案,将事件传播分为两个阶段:
1. 捕获阶段
用户点击蓝色方块,开始事件的捕获阶段:
- 查找蓝色方块的任何祖先元素是否注册了
onclick捕获阶段的处理函数。 - 从最外层的元素开始(绿色方块),如果找到,则执行相应的处理函数。
- 事件继续向内传播,依次执行各个元素上的事件处理函数,直到目标元素(蓝色方块)。
2. 冒泡阶段
事件向下传递到目标元素(即蓝色方块),没有更多的捕获阶段回调函数要处理,事件转移至冒泡阶段:
- 查找当前元素是否已注册冒泡阶段处理函数,有则执行。
- 事件再次向外传播并执行对应元素上冒泡阶段处理函数。
- 直到最外层的祖先元素(绿色方块),事件冒泡结束。
事件使用
冒泡阶段回调函数
可以通过下面三种方式添加冒泡阶段回调函数:
- 直接在标签中使用
onclick属性绑定:
<body>
<div id="box1" onclick="(()=>console.log('green box'))()">
<div id="box2" onclick="(()=>console.log('coral box'))()">
<div id="box3" onclick="(()=>console.log('blue box'))()"></div>
</div>
</div>
</body>
- 使用
onclick方法绑定:
<script type="text/javascript">
window.onload = () => {
const box1 = document.getElementById('box1')
const box2 = document.getElementById('box2')
const box3 = document.getElementById('box3')
box1.onclick = () => {
console.log('green box')
}
box2.onclick = ()=> {
console.log('coral box')
}
box3.onclick = () => {
console.log('blue box')
}
}
</script>
- 使用
addEventListener方法绑定:
<script type="text/javascript">
window.onload = () => {
const box1 = document.getElementById('box1')
const box2 = document.getElementById('box2')
const box3 = document.getElementById('box3')
box1.addEventListener('click', () => {
console.log('green box')
})
box2.addEventListener('click', () => {
console.log('coral box')
})
box3.addEventListener('click', () => {
console.log('blue box')
}, false)
}
</script>
⚠️注意:
- 标签的
onclick属性和onclick方法注册的是冒泡阶段回调函数。 addEventListener方法在不指定第三个参数或第三个参数为false时,默认注册的是冒泡阶段回调函数。
取消事件冒泡
可以通过下面设置来阻止事件传播:
- 【已弃用,仍有部分浏览器支持】将事件对象
cancelBubble属性设置为true:
<script type="text/javascript">
window.onload = () => {
const box1 = document.getElementById('box1')
const box2 = document.getElementById('box2')
const box3 = document.getElementById('box3')
box1.addEventListener('click', () => {
console.log('green box')
})
box2.addEventListener('click', () => {
console.log('coral box')
})
box3.addEventListener('click', (e) => {
console.log('blue box')
e.cancelBubble = true
})
}
</script>
- 调用事件对象
stopPropagation方法:
<script type="text/javascript">
window.onload = () => {
const box1 = document.getElementById('box1')
const box2 = document.getElementById('box2')
const box3 = document.getElementById('box3')
box1.addEventListener('click', () => {
console.log('green box')
}, false)
box2.addEventListener('click', () => {
console.log('coral box')
}, false)
box3.addEventListener('click', (e) => {
console.log('blue box')
e.stopPropagation()
}, false)
}
</script**>**
通过上述设置,事件在处理完蓝色方块的回调函数后就会停止传播,祖先元素的回调函数将不会被执行。
捕获阶段回调函数
通过指定addEventListener方法的第三个参数为true,注册捕获阶段回调函数:
<script type="text/javascript">
window.onload = () => {
const box1 = document.getElementById('box1')
const box2 = document.getElementById('box2')
const box3 = document.getElementById('box3')
box1.addEventListener('click', () => {
console.log('green box')
}, true)
box2.addEventListener('click', () => {
console.log('coral box')
}, true)
box3.addEventListener('click', () => {
console.log('blue box')
}, true)
}
</script>