JS-事件冒泡和事件捕获

事件流

事件流描述的就是从页面中接收事件的顺序。而 IE 和 Netscape 提出了完全相反的事件流概念。IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。

事件冒泡

IE的事件流叫做事件冒泡。即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。所有现代浏览器都支持事件冒泡,并且会将事件一直冒泡到window对象。

事件捕获

事件捕获的思想是不太具体的节点应该更早的接收到事件,而在最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预定目标之前捕获它。IE9+、Safari、Chrome、Opera和Firefox支持,且从window开始捕获(尽管DOM2级事件规范要求从document)。由于老版本浏览器不支持,所以很少有人使用事件捕获。

addEventListener的第三个参数

addEventListener的第三个参数就是为冒泡和捕获准备的.
addEventListener有三个参数:

1
element.addEventListener(event, function, useCapture)

第一个参数是需要绑定的事件
第二个参数是触发事件后要执行的函数
第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。

冒泡的案例

1
2
3
4
5
6
7
8
9
10
11
<div id="click_div">
<p id="click_p">click me!</p>
</div>
<script type="text/javascript">
click_div.addEventListener("click",function(e){
console.log("div 冒泡事件");
},false);
click_p.addEventListener("click",function(e){
console.log("p 冒泡事件");
},false);
</script>

当我们点击click_p的时候,执行结果如下:p 冒泡事件 -> div 冒泡事件

捕获的案例

1
2
3
4
5
6
7
8
9
10
11
12
<div id="click_div">
<p id="click_p">click me!</p>
</div>
<script type="text/javascript">
click_div.addEventListener("click",function(e){
console.log("div 捕获事件");
},true);

click_p.addEventListener("click",function(e){
console.log("p 捕获事件");
},true);
</script>

当我们点击click_p的时候,执行结果如下:div 捕获事件 -> p 捕获事件

事件捕获vs事件冒泡

当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。

这里记被点击的DOM节点为target节点

  1. document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行
  2. 到达target节点,触发事件(到达target节点,触发事件(对于target节点上,捕获与冒泡事件的执行顺序取决于注册顺序)
  3. target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="click_div">
<p id="click_p">click me!</p>
</div>
<script type="text/javascript">
click_div.addEventListener("click",function(e){
console.log("div 冒泡事件");
},false);
click_p.addEventListener("click",function(e){
console.log("p 冒泡事件");
},false);

click_div.addEventListener("click",function(e){
console.log("div 捕获事件");
},true);

click_p.addEventListener("click",function(e){
console.log("p 捕获事件");
},true);
</script>

当我们点击click_p的时候,执行结果如下:div 捕获事件 -> p 冒泡事件 -> p 捕获事件 -> div 冒泡事件

阻止冒泡和捕获

方法一:
cancelBubble 已经不在标准中了,相信迟到会移除这一特性的。

1
event.cancelBubble = true; // IE下阻止冒泡、捕获

方法二:
event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。

1
event.stopPropagation() // 其它浏览器下阻止冒泡、捕获

阻止冒泡的案例

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="click_div">
<p id="click_p">click me!</p>
</div>
<script type="text/javascript">
click_div.addEventListener("click",function(e){
console.log("div 冒泡事件");
},false);
click_p.addEventListener("click",function(e){
// e.cancelBubble = true
e.stopPropagation();
console.log("p 冒泡事件");
},false);
</script>

当我们点击click_p的时候,执行结果如下:p 冒泡事件

阻止捕获的案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="click_div">
<p id="click_p">click me!</p>
</div>
<script type="text/javascript">
click_div.addEventListener("click",function(e){
// e.cancelBubble = true
e.stopPropagation();
console.log("div 捕获事件");
},true);

click_p.addEventListener("click",function(e){
console.log("p 捕获事件");
},true);
</script>

当我们点击click_p的时候,执行结果如下:div 捕获事件

参考文献1
参考文献2