DOM事件模型

2017-08-28

一、事件与事件流

事件是与浏览器或文档交互的瞬间,如点击按钮,填写表格等,它是JS与HTML之间交互的桥梁。
DOM是树形结构,如果同时给父子节点都绑定事件时,当触发子节点的时候,这两个事件的发生顺序如何决定?这就涉及到事件流的概念,它描述的是页面中接受事件的顺序。

事件流描述的是从页面中接收事件的顺序。

IE的事件流是事件冒泡:事件开始时由具体元素接收,然后逐级向上传播直到document对象:即 div——body——html——document;
IE9+/现代浏览器支持事件捕获:由document对象先接收到事件,然后沿DOM树依次向下一直传播到事件的实际目标:即 document——html——body——div;

DOM事件流规定的三个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段。

从DOM事件流模型可以看出,捕获阶段的事件处理函数,一定比冒泡阶段的事件处理函数先执行。

二、事件处理程序

DOM0级事件处理程序

● 写在html标签内,缺点:不好维护

● 将一个函数赋值给一个事件处理属性
例:btn.onClick = function() {}

删除DOM0事件处理程序,只要将对应事件属性置为null即可。
btn.onclick = null;

DOM2级事件处理程序

DOM2级事件定义了两个方法:addEventListener()和removeEventListener()。
接收三个参数:type、handler、useCapture
useCapture=false意味着:将事件处理函数加入到冒泡阶段,在冒泡阶段被调用
useCapture=true意味着:将事件处理函数加入到捕获阶段,在捕获阶段被调用
同一个事件处理函数可以绑定2次,一次用于事件捕获,一次用于事件冒泡。

IE下(DOM2)事件处理程序

attachEvent()添加事件 detachEvent()删除事件
接收相同的两个参数:’on‘ + type 、handler
IE8级更早版本只支持冒泡型事件,所以attachEvent添加的事件都会被添加到冒泡阶段。
IE下使用attachEvent()绑定事件时,事件处理函数中的this指的是window对象。

跨浏览器的事件处理程序

跨浏览器的事件处理函数:

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
var eventUtil = {
// 添加句柄
addHandler: function(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, handler);
} else {
element['on' + type] = handler;
}
},
// 删除句柄
removeHandler: function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent('on' + type, handler);
} else {
element['on' + type] = null;
}
},
//获取事件
getEvent: function(event) {
return event ? event : window.event;
},
//获取事件类型
getType: function(event) {
return event.type;
},
//获取事件源
getElement: function(event) {
return event.target || event.srcElement;
},
//阻止默认事件比如a链接跳转
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
//阻止事件冒泡
stopPropagation: function(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
}

DOM0和DOM2事件的区别

  1. DOM0中事件一旦发生就直接调用事件句柄,无传播过程。在DOM2中有一个事件的传播过程。包括事件捕获,目标元素的事件处理程序运行,事件冒泡。
  2. 一个DOM对象注册多个类型相同的事件时,DOM0级中会发生事件的覆盖,而DOM2级中则会依次执行各个事件函数。

事件处理函数的执行顺序:

捕获阶段的处理函数最先执行,其次是目标阶段的处理函数,最后是冒泡阶段的处理函数。目标阶段的处理函数,先注册的先执行,后注册的后执行。

当用户通过鼠标操作触发 click 事件时,基本的事件触发流程为:
MouseDown 事件 –>( ie下Focus事件)–> MouseUp 事件 –> Click 事件。

若被点击的元素可以获得焦点,并且当前还没有获得焦点时,会在 MouseDown 事件之后默认触发 Focus 事件,再依次触发其后的 MouseUp 和 Click 事件。
非 IE 浏览器中,此情况下元素无法获得焦点,也不会触发 Focus 事件。

三、事件对象

DOM中的事件对象属性

○ type: 获取事件类型
○ target:事件目标
○ stopPropagation() 阻止事件传播(冒泡+捕获)
○ preventDefault() 阻止事件的默认行为

IE中的事件对象属性

○ type: 获取事件类型
○ srcElement:事件目标
○ cancelBubble=true  阻止事件冒泡
○ returnValue=false   阻止事件的默认行为

IE8以及以前可以通过 window.event.cancelBubble=true阻止事件的冒泡;
IE9+/FF/Chrome通过event.stopPropagation()阻止事件的继续传播(冒泡或捕获都支持)。

四、事件委托

事件委托利用了事件冒泡,只用一个事件处理程序就可以管理某一类型的所有事件。

使用场景:

  1. 给许多子元素绑定相同的事件,比如ul的li元素,或者table的td元素。可以大量节省内存,减少事件注册。
  2. 可以实现当新增子对象时无需再次绑定事件,对于动态内容极其合适。

优点:

  • document对象很快就可以访问,而且可以在页面周期的任何时段为它添加事件处理程序。只要可单击的元素呈现在页面上,就可以立即具备适当的功能。
  • 在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花时间也更少。
  • 整个页面占用内存空间更少,能够提升整体性能。

缺点:

如果把所有事件都用事件代理,可能会出现本不该被触发的事件被绑定上了事件的情况。