事件流
事件流是在浏览器发展到第四代(IE4及Netscape Communicator4)时出现的概念,事件流描述的是从页面接收事件的顺序。但有意思的是,IE和Netscape开发团队居然提出了完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。
事件冒泡
事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点,也可以说是真实触发节点)接收,然后逐级向上传播到较为不具体的节点(文档:document)
<!DOCUTYPE html>
<html>
<head>
</head>
<body>
<div id="div">Click Me</div>
</body>
<html>
点击div整个事件的传播
IE9、Firefox、Chrome、Safari会一直冒泡到window对象
事件捕获
事件捕获(event capturing),即不太具体的节点应该更早接收到事件,而最具体的节点(真实触发节点)应该最后接收到事件,仍以之前的HTML为例,事件传递顺序:
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出相应。 仍以前面的HTML页面为例,事件触发顺序:
在
DOM2
事件流中,实际的目标(div
元素)在捕获阶段不会接收到事件。意味着在捕获阶段,事件从document
到<html>
再到<body>
后就停止了。下一个阶段是“处于目标”阶段,于是事件在<div>
上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回文档。多数支持DOM事件流的浏览器都实现了一种特定的行为;即使“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但IE9、Safari、Chrome、Firefox和Opera 9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。
DOM2级事件处理程序
“
DOM 2级事件
”定义了两个方法,addEventListener()
绑定指定事件处理程序、removeEventListener()
删除指定事件处理程序。所有DOM
节点中都包含这两个方法,都接受3个参数:(event,function,boolean
)。最后布尔值参数如果是true
,表示在捕获阶段调用事件处理函数;如果为false
,表示在冒泡阶段调用事件处理程序;默认为false。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
上面的代码为一个按钮添加了onclick事件处理程序,而且该事件会在冒泡阶段被触发(因为最后一个参数是
false
)。使用DOM2
级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
这里为按钮添加了两个事件处理程序。这两个事件处理程序会按照添加它们的顺序触发,因此首先会显示元素的ID,其次会显示”Hello world!”消息。
通过addEventListener()
添加的事件处理程序只能使用removeEventListener()
来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()
添加的匿名函数将无法移除,如下面的例子所示。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
//这里省略了其他代码
btn.removeEventListener("click", function(){ //没有用!
alert(this.id);
}, false);
在这个例子中,我们使用
addEventListener()
添加了一个事件处理程序。虽然调用removeEventListener()
时看似使用了相同的参数,但实际上,第二个参数与传入addEventListener()
中的那一个是完全不同的函数。而传入removeEventListener()
中的事件处理程序函数必须与传入addEventListener()
中的相同,如下面的例子所示。
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!
重写后的这个例子没有问题,是因为在
addEventListener()
和removeEventListener()
中使用了相同的函数。多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,我们不建议在事件捕获阶段注册事件处理程序。
事件对象
兼容DOM的浏览器会将一个
event
对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0级
或DOM2级
),都会传入event
对象。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
alert(event.type); //"click"
}, false);
- 事件对象的属性
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
bubbles | Boolean | 只读 | 表明事件是否冒泡 |
cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 |
currentTarget | Element | 只读 | 其事件处理程序当前正在处理事件的那个元素 |
defaultPrevented | Boolean | 只读 | 为true表示已经调用了preventDefault()(DOM3级事件中新增) |
detail | Integer | 只读 | 与事件相关的细节信息 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段 |
preventDefault() | Function | 只读 | 取消事件的默认行。如果cancelable是true,则可以使用这个方法 |
stopImmediatePropagation | Function | 只读 | 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM 3级事件中新增) |
stopPropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法 |
trusted | Boolean | 只读 | 为true表示事件时浏览器生成的。为false表示事件使用开发人员通过JavaScript创建的(DOM3级事件中新增) |
type | String | 只读 | 被触发的事件的类型 |
target | Element | 只读 | 事件的目标 |
timeStamp | Number | 只读 | 返回事件生成的日期和时间 |
view | AbstractView | 只读 | 与事件关联的抽象视图。等同于发生事件的window对象 |
在事件处理程序内部,对象
this
始终等于currentTarget
的值,而target
则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget
和target
包含相同的值。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this); //true
alert(event.target === this);
//true
};
部分内容出自《JavaScript高级程序设计》第三版,欢迎指出错误点