事件模型实践

2016-10-25

浏览器事件模型

缺陷

1.说起浏览器的原生的事件模型,首先印入脑海的是onclick,onload等事件,然后才是addEventListener,removeEventListener,确实在平常的编码过程中接触到更多的是封装过的on,off之流。
2.在日常处理click等事件之时,会使用到阻止冒泡(event.stopPropagation),阻止默认行为(event.preventDefault)或者直接reture false,但却没有真正去深入了解事件的传递机制。

发现

今天阅读的时获取了很多新知识,在此作记录

首先是html

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
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<style type="text/css">
#parent{
width: 100px;
height: 100px;
background-color: green;
}
#child{
width: 50px;
height: 50px;
background-color: blue;
}
</style>
</head>

<body>
<div id='parent'>
<p id="child"></p>
</div>
</body>

</html>

大概就这样
desc

然后添加事件,从window,document,html,body,parent,child依次添加捕获与冒泡事件的监听,并打印出事件的目标节点和当前节点。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<script>
var parent = document.getElementById("parent");
var child = document.getElementById("child");

// 捕获阶段绑定事件
window.addEventListener("click", function(e){
console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

document.addEventListener("click", function(e){
console.log("document 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

document.documentElement.addEventListener("click", function(e){
console.log("documentElement 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

document.body.addEventListener("click", function(e){
console.log("body 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

parent.addEventListener("click", function(e){
console.log("parent 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

child.addEventListener("click", function(e){
console.log("child 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

// 冒泡阶段绑定的事件
window.addEventListener("click", function(e){
console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

document.addEventListener("click", function(e){
console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

document.documentElement.addEventListener("click", function(e){
console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

document.body.addEventListener("click", function(e){
console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

parent.addEventListener("click", function(e){
console.log("parent 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

child.addEventListener("click", function(e){
console.log("child 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

</script>

点击蓝色的p标签,控制台信息
result1
是真的先捕获再冒泡吗?还是因为事件绑定的顺序?

交换捕获和冒泡的绑定顺序之后
result2
出现child的捕获冒泡顺序发生变化,原来这就是目标阶段了,当targetcurrentTarget相等之时就进入目标阶段,这时候绑定的事件是按绑定顺序依次触发的,所以在child上写的”捕获”,”冒泡”应该都改为”捕获”才对。

再做个验证:在所有事件绑定之前,添加

1
2
3
4
5
6
7
8
9
10
child.onclick = function(){
console.log("child onclick");
}
parent.onclick = function(){
console.log("parent onclick");
}

window.onclick = function(){
console.log("window onclick");
}

点击得到以下结果:
result3

总结

1.事件的捕获是从上至下触发了window,document,documentElement, body,parent绑定的事件。
2.当来到child节点时,是目标阶段,事件按照绑定顺序依次触发。
3.冒泡阶段与捕获阶段相反,自下而上的传递,而一些onclick之类的事件也都在该阶段触发,但是为啥在这个阶段呢?看下一条。
4.IE的事件流中只存在冒泡这个阶段,所以为了跟好地兼容IE。。。至于那个只有捕获阶段的网景,有个了解就好。
5.到这里,标准的浏览器事件已经梳理地差不多了,可能在平时编码过程中的影响不是很大,对它的实现原理的了解不是多多益善吗?


Comments: