关于事件流,事件冒泡和事件捕获
1.事件流
假设有这样一个场景:
有一个导航条:div > ul > li > a,每个元素块宽高一样,就像是一组同心圆。如果我们点击a元素,那么浏览器会认为单击事件不仅仅发生在a上。换句话说,在我们单击a元素的同事,我们也单击了容器中的其他元素。
事件流描述的是从页面中接收事件的顺序。但有意思的是,IE 和 Netscape 却分别提出了不一样的事件流概念。IE 的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。
那么问题来了,什么是事件冒泡?什么是事件捕获?
2.事件冒泡
事件冒泡:就是说事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。
<!DOCTYPE html> <html> <head> <title>example</title> </head> <body> <div>click</div> </body> <html>
以上代码,如果我们点击页面中的<div>,那么这个click事件会按照以下顺序传播:
<div> => <body> => <html> => document
3.事件捕获
事件捕获:与事件冒泡差不多完全相反,事件捕获思想是从不太具体的节点应该更早接收到事件,而具体的节点应该是最后接收到事件。
那么如果我们点击页面中的<div>,那么这个click事件会按照以下顺序传播:
document => <html> => <body> => <div>
虽然事件捕获是Netscap唯一支持的事件流模型,但是IE9/safari/chrome/opera/firefox目前都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但是这些浏览器都是从window对象开始捕获事件的。
那么问题又来了,什么是“DOM2级事件”?
4.DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段--处于目标阶段--事件冒泡阶段。
还是用上面的代码为例:
document => <html> => <body> => <div> => <body> => <html> => document
捕获阶段 处于目标阶段 冒泡阶段
在DOM事件流中,实际目标在捕获阶段是不会接收到事件的。这意味着捕获阶段到<body>就停止了。下一个阶段是“处于目标阶段”,即事件在<div>上发生,而这一个阶段被堪称冒泡阶段的一部分。继而冒泡阶段发生,事件传回document。
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener( )和removeEventListener( )。所有的DOM节点中都包含这两个方法,并且它们接收3个参数:要处理的事件,作为事件处理程序的函数和一个布尔值。布尔值如果是true,表示在捕获阶段调用事件处理程序,如果是false,表示在冒泡阶段调用事件处理程序。
var btn = document.getElementIdBy("mybtn");
btn.addEventListener("click",function(){
aliert(this.id);
},false);
btn.removeListener("click",function(){
aliert(this.id);
},false);
注意:以上代码中的函数为匿名函数,将无法移除匿名函数,因为这完全是两个不同的函数。
var btn = document.getElementIdBy("mybtn");
var handler = function(){
aliert(this.id);
}
btn.addEventListener("click",handler,false);
btn.removeListener("click",handler,false);
这样就没问题了。
另外,应该尽可能把事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。