蓝戒的博客


js事件流:冒泡事件和捕获事件详解

文章目录
1.什么是事件流
2.事件流的类型
3.冒泡型事件(Dubbed Bubbling)
4.捕获型事件(Event Capturing)
5.事件处理函数/监听函数
6.事件注册
7.现代事件绑定方法
8.事件委托

什么是事件流

从页面中接收事件的顺序。也就是说当一个事件产生时,这个事件的传播过程,就是事件流。
浏览器中的事件流意味着页面上可有不仅一个,甚至多个元素响应同一个事件。而这一个或多个元素响应事件发生的先后顺序在各个浏览器(主要针对IE和Netscape)上是不同的。

事件流的类型:

DOM同时支持两种事件模型:捕获型事件和冒泡型事件。DOM事件标准定义了冒泡和捕获两种事件流,这两种事件流有着显著的不同并且可能对你的应用有着相当大的影响。和许多Web技 术一样,在它们成为标准之前,Netscape和微软各自不同地实现了它们。Netscape选择实现了捕获事件流,微软则实现了冒泡事件流。幸运的 是,W3C决定组合使用这两种方法,并且大多数新浏览器都遵循这两种事件流方式。
每当某一事件发生时,都会经过捕获阶段->处理阶段->冒泡阶段(有些浏览器不支持捕获)
20121031173236106
捕获阶段是由上层元素到下层元素的顺序依次。而冒泡阶段则正相反。
当事件触发时body会先得到有事件发生的信息,然后依次往下传递,直到到达最详细的元素。这就是事件捕获阶段。
还记得事件注册方法ele.addEventListener(type,handler,flag)吧,Flag是一个Boolean值,true表示事件捕捉阶段执行,false表示事件冒泡阶段执行。
接着就是事件冒泡阶段。从下往上 依次执行事件处理函数(当然前提是当前元素为该事件注册了事件句柄)。
在这个过程中,可以阻止事件的冒泡,即停止向上的传递。

冒泡型事件(Dubbed Bubbling)

事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。
IE上的解决方案就是冒泡型事件(Dubbed Bubbling)。冒泡型事件的基本思想是,事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。
示例(1)的XHTML代码结构:

<span id="cnt1">
<a href=”#1″ id=”link1″>点击我触发捕获型事件流</a>
</span>

这个示例里我同时给span和a标签绑定了click事件,大家看到了,我们点击这个a标签,一次点击(click)同时触发了两个元素a和span的事件。先触发的是绑定给a标签的click事件,然后触发的是span标签的click事件。也就是前面提到的“页面上可有不仅一个,甚至多个元素响应同一个事件”和“事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发”。
这个示例里我们点击的第一目标是a标签这个链接,它就是前面提到的“最特定的事件目标”,然后才是span这个相对“不特定的事件目标”。再看看我的XHTML代码结构,你会发现a标签包含在span标签中,也就是说span是a标签的父节点,如果大家还不是很清晰的知道他们之间的关系,让我们看看下面的这个DOM树的结构图:
示例(1)
事件触发的顺序是从最特定的目标,沿着DOM树不断的向上触发click事件,就像气泡从下一直上冒的过程一样。“冒泡型”也就是这么得来的,也很形象。这里要说明的是,由于我只给a和span绑定了click事件,所以“冒”到span就到顶了,如果你也给包含他们的p标签还有document绑定click事件,这个冒泡的过程就会一直延续到document的事件触发才结束。
另外要说明的是,在IE5.5中冒泡的最高层DOM节点为document,IE6中则可以支持html节点。在Mozllia1.0及之后的版本也支持冒泡,而它则更可以冒到window窗口对象。
完整演示案例:

捕获型事件(Event Capturing)

相对IE4.0,Netscape4.0则使用的是捕获型事件的解决方案。这个事件触发的过程则正好和冒泡相反——在捕获型事件中,事件从最不精确的对象(document对象)开始触发,然后到最精确的对象。还是前面的示例,不过现在换由捕获型事件触发(当然你需要在Netscape或Firefox中测试)。
示例(2)的XHTML代码结构:

<span id="cnt1">
<a id="”link1″" href="”#1″">点击我触发捕获型事件流</a>
</span>

事件触发的循序正好跟前面的冒泡相反,这里我就不赘述了。
DOM 事件流
这个事件流则是W3C制定一个标准规范,它同时支持两种事件流模式,不过是先发生捕获型事件流,再发生冒泡型事件流。
DOM事件流最独特的是,它支持文本节点也触发事件(IE中这不支持)。不过说实话,我现在还看不出来让文本节点支持事件有什么作用。
最后要说的是,根据最近大家在开发的实践过程中的运用,我们一般都采取冒泡型的事件流触发方式,这点我们的IE做的比较成功。

示例图:
event-1

事件处理函数/监听函数

事件注册

平常我们绑定事件的时候用dom.onxxxx=function(){}的形式
这种方式是给元素的onxxxx属性赋值,只能绑定有一个处理句柄。
但很多时候我们需要绑定多个处理句柄到一个事件上,而且还可能要动态的增删某个处理句柄
下面的事件注册方式就能解决这个需求。
先介绍一下四个方法 :

//IE以外
target.addEventListener(type,listener,useCapture)
target.removeEventListener(type,listener,useCapture);
target :文档节点、document、window 或 XMLHttpRequest。
type :字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。
listener :实现了 EventListener 接口或者是 JavaScript 中的函数。
useCapture :是否使用捕捉,一般用 false。
//IE
target.attachEvent(type, listener);
target.detachEvent(type, listener);
target :文档节点、document、window 或 XMLHttpRequest。
type :字符串,事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等。
listener :实现了 EventListener 接口或者是 JavaScript 中的函数。
两者使用的原理:可对执行的优先级不一样,实例讲解如下:
ele.attachEvent("onclick",method1);
ele.attachEvent("onclick",method2);
ele.attachEvent("onclick",method3);
执行顺序为method3->method2->method1
ele.addEventListener("click",method1,false);
ele.addEventListener("click",method2,false);
ele.addEventListener("click",method3,false);
执行顺序为method1->method2->method3
兼容后的方法
var func = function(){};
//例:addEvent(window,"load",func)
function addEvent(elem, type, fn) {
if (elem.attachEvent) {
elem.attachEvent('on' + type, fn);
return;
}
if (elem.addEventListener) {
elem.addEventListener(type, fn, false);
}
}
//例:removeEvent(window,"load",func)
function removeEvent(elem, type, fn) {
if (elem.detachEvent) {
elem.detachEvent('on' + type, fn);
return;
}
if (elem.removeEventListener) {
elem.removeEventListener(type, fn, false);
}
}

现代事件绑定方法

使用传统事件绑定有许多缺陷,比如不能在一个对象的相同事件上注册多个事件处理函数。而浏览器和W3C也并非没有考虑到这一点,因此在现代浏览器中,它们有自己的方法绑定事件。
W3C DOM
obj.addEventListener(evtype,fn,useCapture)——W3C提供的添加事件处理函数的方法。obj是要添 加事件的对象,evtype是事件类型,不带on前缀,fn是事件处理函数,如果useCapture是true,则事件处理函数在捕获阶段被执行,否则 在冒泡阶段执行
obj.removeEventListener(evtype,fn,useCapture)——W3C提供的删除事件处理函数的方法
微软IE方法
obj.attachEvent(evtype,fn)——IE提供的添加事件处理函数的方法。obj是要添加事件的对象,evtype是事件类型,带on前缀,fn是事件处理函数,IE不支持事件捕获
obj.detachEvent(evtype,fn,)——IE提供的删除事件处理函数的方法,evtype包含on前缀

整合两者的方法:

function addEvent(obj,evtype,fn,useCapture) {

if (obj.addEventListener) {
obj.addEventListener(evtype,fn,useCapture);
} else {
obj.attachEvent("on"+evtype,fn);//IE不支持事件捕获
} else {
obj["on"+evtype]=fn;//事实上这种情况不会存在
}
}
function delEvent(obj,evtype,fn,useCapture) {
if (obj.removeEventListener) {
obj.removeEventListener(evtype,fn,useCapture);
} else {
obj.detachEvent("on"+evtype,fn);
} else {
obj["on"+evtype]=null;
}
}

但IE的attachEvent方法有另外一个问题,同一个函数可以被注册到同一个对象同一个事件上多次,解决方法:抛弃IE的 attachEvent方法吧!IE下的attachEvent方法不支持捕获,和传统事件注册没多大区别(除了能绑定多个事件处理函数),并且IE的 attachEvent方法存在内存泄漏问题!

addEvent,delEvent现代版示例:

事件委托

例如,你有一个很多行的大表格,在每个<tr>上绑定点击事件是个非常危险的想法,因为性能是个大问题。流行的做法是使用事件委托。

事件委托描述的是将事件绑定在容器元素上,然后通过判断点击的target子元素的类型来触发相应的事件。
事件委托依赖于事件冒泡,如果事件冒泡到table之前被禁用的话,那以下代码就无法工作了。

myTable.onclick = function () {
e = e || window.event;
var targetNode = e.target || e.srcElement;
// 测试如果点击的是TR就触发
if (targetNode.nodeName.toLowerCase() === 'tr') {
alert('You clicked a table row!');
}
}

参考资料:
1、http://www.jb51.net/article/31528.htm
2、http://www.jb51.net/article/19163.htm
3、http://www.jb51.net/article/53438.htm
4、http://www.jb51.net/article/19163.htm
5、http://www.2cto.com/kf/201504/391774.html

本文固定链接: http://www.webzsky.com/?p=655 | 蓝戒的博客

cywcd
该日志由 cywcd 于2015年08月07日发表在 javascript 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: js事件流:冒泡事件和捕获事件详解 | 蓝戒的博客
关键字: , ,

js事件流:冒泡事件和捕获事件详解:等您坐沙发呢!

发表评论


快捷键:Ctrl+Enter
来自的朋友,欢迎您 点击这里 订阅我的博客 o(∩_∩)o~~~
×