DOM事件模型

目前DOM标准有DOM1、DOM2、DOM3、DOM4,其中DOM4为草案,尚未推行,主要使用的是DOM3标准。

一、DOM1

DOM1分为DOM核心(Object Model Core)和HTML的DOM(Object Model HTML),DOM1是对DOM0的归纳整理概括,
DOM1支持的事件有:blur、click、focus、select这几个事件。

二、DOM2

DOM2包括DOM核心、视图、事件、样式、遍历范围和HTML。DOM2中的事件标准已经非常完善,在DOM3中并未对此进行修改,所以至今针对DOM的事件是以DOM2事件标准为基准的。
这里主要讲一下DOM Level2的事件标准。
在开始之前,来看一段代码:
DOM
以上的事件绑定,对于行内事件绑定B、C是正确的,对于JS代码中的事件绑定,事件一是正确的。
在行内绑定事件中,onclick=”要绑定的事件”,一旦用户点击,浏览器就eval(“要执行的代码”)。其中:
eval("print")只会将print方法打印出来。
eval("print()")会执行print()方法内的语句
eval("print.call()")的相当于eval(“print()”)
在JS代码中的事件绑定,一旦用户点击,那么浏览器就执行:x.onclick.call(this,arguments,{…})即点击后应该执行一个方法,
x.onclick=print直接将方法赋值到点击事件上,可执行
y.onclick=print() print()执行完无返回值,则返回undefined,所以此时y.onclick = undfined,所以错误
z.onclick=print.call()同上
以上是DOM Level1的事件绑定方法,下面正式介绍DOM Level2的事件绑定

1、click事件绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
......
<button id=xxx></button>
......
//======JS======
xxx.onclick = function(){
console.log(1);
}
xxx.onclick = function(){
console.log(2);
}
//以上事件绑定中,点击按钮只会输出`2` ,后面的点击事件会覆盖前面的点击事件,此处onclick为属性,唯一,后面重复定义只会覆盖前面的值
//以下是DOM2写法
xxx.addEventListener('click',function(){

})

addEventListener绑定事件可以多次触发,先绑定先触发,后绑定后触发,执行顺序类似于栈的执行顺序,先进先出后进后出,即先定义先执行后定义后执行。
如果绑定多事件:

1
2
3
4
5
6
7
8
9
function f1(){
console.log(1);
}
function f2(){
console.log(2);
}
xxx.addEventListener('click',f1);
xxx.addEventListener('click',f2);
xxx.removeEventListener('click',f1); //移除事件

2、事件冒泡与捕获

1) 什么是事件

JavaScriptHTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。

2) 事件流

事件流描述的是从页面中接收事件的顺序。但有意思的是,IE 和 Netscape 开发团队居然提出了差不多是完全相反的事件流的概念。IE 的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事件捕获流

事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。以下面的 HTML 页面为例:

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>

当我们单击了页面中的div元素时,click事件的传播顺序为:
<div>-><body>-><html>->document
click事件首先会在div元素上发生,而这个元素就是我们单击的元素。然后click事件沿着DOM树向上传播,在每一级节点上都会发生,直至传播到document对象。如下图所示:
事件冒泡
IE9FirefoxChromeSafari将事件一直 冒泡到window对象。

事件捕获
Netscape Communicator 团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。如果仍以前面的 HTML 页面作为演示事件捕获的例子,那么单击<div>元素就会以下列顺序触发 click 事件:
document-><html>-><body>-><div>
在事件捕获过程中,document对象首先接收到 click 事件,然后事件沿 DOM 树依次向下,一直传播到事件的实际目标,即<div>元素。事件捕获的过程如下图所示:
事件捕获
虽然事件捕获是 Netscape Communicator 唯一支持的事件流模型,但 IE9、Safari、Chrome、Opera 和 Firefox 目前也都支持这种事件流模型。尽管“DOM2 级事件”规范要求事件应该从 document 对象 开始传播,但这些浏览器都是从 window 对象开始捕获事件的。
由于老版本的浏览器不支持,因此很少有人使用事件捕获。

3)DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段处于目标阶段事件冒泡阶段。首 先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶 段,可以在这个阶段对事件做出响应。以前面简单的 HTML 页面为例,单击<div>元素会按下图所示顺序触发事件。
DOM2级事件
在 DOM 事件流中,实际的目标(<div>元素)在捕获阶段不会接收到事件。这意味着在捕获阶段事件从 document<html>再到<body>后就停止了。下一个阶段是“处于目标”阶段,于是事件在 <div> 上发生,并在事件处理(后面将会讨论这个概念)中被看成冒泡阶段的一部分。然后,冒泡阶段发生, 事件又传播回文档。

4)例子
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
//结构,默认已经引入jQuery
...
<div id="red">
<div id="pink">
<div id="yellow"></div>
</div>
</div>
...
red.addEventListener('click',function(){
console.log('red');
})
pink.addEventListener('click',function(){
console.log('pink');
})
yellow.addEventListener('click',function(){
console.log('yellow');
})
//以上为事件冒泡,点击`yellow`时会依次输出:yellow->pink->red
red.addEventListener('click',function(){
console.log('red');
},true)
pink.addEventListener('click',function(){
console.log('pink');
},true)
yellow.addEventListener('click',function(){
console.log('yellow');
},true)
//以上为事件捕获,点击`yellow`时会依次输出:red->pink->yellow
//若要阻止冒泡,可在代码中加入:
pink.addEventListener('click',function(e){
e.stopPropagation();
console.log('pink');
})
//以上,点击`yellow`时会依次输出:yellow->pink
  • 本文主要参考《JavaScript高级程序设计》第13章