首先,先用一张图(以HTML-->button为例)简单了解一下DOM事件传播机制的基本原理:
一个事件从触发到响应,会经历两个阶段(挖洞和冒泡的过程):
挖洞:先从根标签开始,逐层向子标签传递这个事件,直到遇见事件目标(button)结束,然后由事件目标开始响应事件,并且调用事件函数。
冒泡:事件目标响应事件,调用事件函数,逐层向父标签传递事件响应,直至HTML根标签结束。
注意:事件函数默认是在冒泡过程中调用的,可以通过addEventListener()设置第三个参数为True,改为挖洞过程调用。对于事件传播流程来看,挖洞总是在冒泡之前执行,对于最里层的事件目标也是如此。(例外的是,在火狐浏览器中,对于事件目标,挖洞函数和冒泡函数谁先绑定执行谁。)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>事件传播机制</title>
</head>
<body>
<div id="div">
<button id="btn">点我</button>
<a href="http://www.baidu.com">百度一下</a>
</div>
<script>
var button = document.getElementById("btn");
var div = document.getElementById("div");
var body = document.body;
var html = document.documentElement;
button.addEventListener("click",function(){ console.log("5,点击button") }, true)
div.addEventListener("click",function(){ console.log("6,点击div") }, true)
body.addEventListener("click",function(){ console.log("7,点击body") }, true)
html.addEventListener("click",function(){ console.log("8,点击html") }, true)
button.addEventListener("click",function(){ console.log("1,点击button") }, false)
div.addEventListener("click",function(){ console.log("2,点击div") }, false)
body.addEventListener("click",function(){ console.log("3,点击body") }, false)
html.addEventListener("click",function(){ console.log("4,点击html") }, false)
// 结果: 打印顺序: 8,7,6,1,5,2,3,4
// 总结:
// 1, 一个事件从触发到响应,经历的流程是: 先由根标签html触发事件,然后一层一层向子标签传递这个事件,直到传递到事件目标(button)结束,这个过程叫挖洞过程, 然后由事件目标(button)开始响应事件,并调用事件函数, 然后一层一层向父标签传递事件响应, 直到html根标签结束, 这个过程叫冒泡过程
// 2, 事件函数默认是在冒牌过程中调用的, 可以通过addEventListener设置第三个参数为true,改为挖洞过程调用
// 3, 对于事件传播流程中的标签来说, 挖洞总是在冒泡之前执行, 对于最里层的事件目标也是如此, 但火狐浏览器不这样认为, 在火狐中,对于事件目标, 挖洞函数和冒泡函数谁先绑定先执行
var a = document.querySelector("a")
// 需求: div中有一个a标签, 点击div背景变蓝,点击a背景变红
// a.addEventListener("click", function(e){
// e.preventDefault() // 阻止默认行为, 也就是a标签跳转
// body.style.backgroundColor = "red"
// });
// div.addEventListener("click", function(){
// body.style.backgroundColor = "blue"
// })
// 结果: 以上写法是错的, 不管点a还是div,都显示蓝色
// 原因: a标签修改红色之后,冒泡给了父标签div,又改成了蓝色
// 解决: 方案一, 阻止a标签的冒泡, 不让div执行冒泡函数
a.addEventListener("click", function(e){
e.preventDefault() // 阻止默认行为, 也就是a标签跳转
body.style.backgroundColor = "red"
// 阻止a标签的事件传播, 不会阻止a自身的冒泡
// e.stopPropagation()
// 立即阻止a标签的事件传播, 会把自身的冒泡也阻止掉
e.stopImmediatePropagation()
console.log("a挖洞")
},true);
a.addEventListener("click", function(e){console.log("a冒泡")}, false)
div.addEventListener("click", function(){
body.style.backgroundColor = "blue"
})
// 解决: 方案二, 让div事件函数在挖洞过程执行, 这样点击a时,a的红色会替换div的蓝色
a.addEventListener("click", function(e){
e.preventDefault() // 阻止默认行为, 也就是a标签跳转
body.style.backgroundColor = "red"
});
div.addEventListener("click", function(){
body.style.backgroundColor = "blue"
},true)
</script>
</body>
</html>