JavaScript事件:捕获 VS 冒泡
在浏览器文档模型DOM中,事件是指因为某种具体的交互行为发生,由被作用的元素发出,再由浏览器响应的过程。常见的事件有:click,onmouseover,onblur等等。
DOM(文档对象模型)结构是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素结点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为DOM事件流。
按照DOM事件流的观点,事件在树中传播的方向就有两个,一个是从根结点向子结点流动(捕获阶段),以及子结点向根结点传播的方向(冒泡阶段)。前者是Netscape Navigator对事件流的实现:捕获型事件(Capturing),后者是IE的实现方式,即冒泡型事件(Bubbling)。
DOM标准事件模型:首先是捕获式传递事件,接着是冒泡式传递。
因为两个不同的模型都有其优点和解释,DOM标准支持捕获型与冒泡型,可以说是它们两者的结合体。它可以在一个DOM元素上绑定多个事件处理器,并且在处理函数内部,this关键字仍然指向被绑定的DOM元素.
在注册事件时主要有三种方法:
1.HTML挂载法
<div onclick=”alert(this.innerHTML);”>内容<div>
<div onclick=”alert(this.innerHTML);”>内容<div>
这是很普遍的做法,主要优点是方便,容易理解。但是最近有观点认为这种方法对html的web语义产生了破坏,建议将事件注册写到js模块中,从而将html与js分离。
2.js赋值法
element.onclick = function(){alert(this.innerHTML);}
element.onclick = function(){alert(this.innerHTML);}
注意:以上两种方法只支持事件冒泡方式,不支持捕获方式。
3.添加事件监听方法(除IE外的浏览器)
element.addEventListener(‘click’, observer, useCapture);
element.addEventListener(‘click’, observer, useCapture);
addEventListener方法接受三个参数。第一个参数是事件名称,值得注意的是,这里事件名称与IE的不同,事件名称是没’on’开头的 ;第二个参数observer是回调处理函数;第三个参数注明该处理回调函数是在事件传递过程中的捕获阶段被调用还是冒泡阶段被调用,true表示注册到捕获阶段,false表示注册到冒泡阶段。
移除已注册的事件监听器调用element的removeEventListener即可,参数不变.
element.removeEventListener(‘click’, observer, useCapture);
element.removeEventListener(‘click’, observer, useCapture);
在IE中使用attachEvent方法来实现:
element.attachEvent(‘onclick’, observer);
element.attachEvent(‘onclick’, observer);
attachEvent接受两个参数。第一个参数是事件名称,第二个参数observer是回调处理函数。这里得说明一下,有个经常会出错的地 方,IE下利用attachEvent注册的处理函数调用时this指向不再是先前注册事件的元素,这时的this为window对象了.IE只支持事件注册到冒泡阶段。
要移除先前注册的事件的监听器,调用element的detachEvent方法即可,参数相同。
element.detachEvent(‘onclick’, observer);
element.detachEvent(‘onclick’, observer);
事件注册到不同阶段的举例,下面是典型的冒泡方式:
<p onclick="javascript:alert('外面的p被点击!')">
<input type="button" value="注册到冒泡阶段,点击我查看效果"
onclick="javascript:alert('里面的button被点击!')"/>
</p>
<p onclick="javascript:alert('外面的p被点击!')"> <input type="button" value="注册到冒泡阶段,点击我查看效果" onclick="javascript:alert('里面的button被点击!')"/> </p>
下面是注册到冒泡阶段的代码,使用了addEventListener方法。注意addEventListener的第三个参数在这里是true,这意味着将事件click注册到了捕获阶段,也就是外面的p将先得到这个事件,然后才是内层的button。(以下例子请采用Firefox或chrome等非 ie浏览器测试)
<div id="out">
<div id="in"> 注册到捕获阶段,点击我查看效果</div>
</div>
<script type="text/javascript">
document.getElementById("out").addEventListener('click',
function(){alert('外面的div被点击!');}, true);
document.getElementById("in").addEventListener('click',
function(){alert('里面的div被点击!');},true);
</script>
<p id="js_out2"> <input type="button" id="js_in2" value="注册到捕获阶段,点击我查看效果"/> </p> <script type="text/javascript"> document.getElementById("js_out2").addEventListener('click', function(){alert('外面的div被点击!');}, true); document.getElementById("js_in2").addEventListener('click',function(){alert('里面的div被点击!');},true); </script>
再放两个例子增加读者对捕获和冒泡的印象。