Home » Code » javascript事件捕获与冒泡

javascript事件捕获与冒泡

javascript中的事件有事件流这么一说,又分为冒泡型和捕获型。冒泡型是从事件节点往上向根节点延伸,代表方是IE;捕获型则相反,从最外根节点(window或document)向事件节点延伸,除IE外的Firefox,Opera等是代表方。不过这些都已经是陈年往事了,如今W3C规定的标准事件流是先捕获后冒泡,默认二者都会发生,其中冒泡可以手动阻止。

对事件进行监听,W3C标准的方法是使用addEventListener(elem, evType, fn, capture),四个参数分别代表要监听的dom元素、事件类型、触发函数、在捕获阶段触发还是冒泡阶段触发(默认为false,表示在冒泡阶段,true为捕获阶段)。同样的老版本的IE(IE8或以下)使用的是attachEvent(),但如今IE都已经发展到11连12就要来了,早已经支持了标准的addEventListener()。

为看清楚所谓的先捕获后冒泡,设计三个div,分别监听每个div在捕获和冒泡阶段的click事件。

<div id="outside">
	最外面
	<div id="middle">
		中间
		<div id="inner">最内部</div>
	</div>
</div>

addEventListener

监听三个div捕获与冒泡阶段的click事件:

function addEvent(obj, evType, fn, capture){
	if(obj.addEventListener){
		obj.addEventListener(evType, fn, capture);//DOM 2.0
		return true;
	}else if(obj.attachEvent){
		var r = obj.attachEvent("on" + evType, fn);//IE5+
		return r;
	}else{
		obj["on" + evType] = fn;//DOM 0
	}
}

addEvent(outside, "click", function(e){
	console.log('outside click,注册于捕获阶段,事件由'+e.target.id+'触发,目前处理该事件的是'+e.currentTarget.id)}, true);
addEvent(outside, "click", function(e){
	console.log('outside click,注册于冒泡阶段,事件由'+e.target.id+'触发,目前处理该事件的是'+e.currentTarget.id)}, false);
addEvent(middle, "click", function(e){
	console.log('midle click,注册于捕获阶段,事件由'+e.target.id+'触发,目前处理该事件的是'+e.currentTarget.id)}, true);
addEvent(middle, "click", function(e){
	console.log('midle click,注册于冒泡阶段,事件由'+e.target.id+'触发,目前处理该事件的是'+e.currentTarget.id)}, false);
addEvent(inner, "click", function(e){
	console.log('inner click,注册于捕获阶段,事件由'+e.target.id+'触发,目前处理该事件的是'+e.currentTarget.id)}, true);
addEvent(inner, "click", function(e){
	console.log('inner click,注册于冒泡阶段,事件由'+e.target.id+'触发,目前处理该事件的是'+e.currentTarget.id)}, false);

以上,addEvent()是为兼容新旧浏览器而封装,很经典的一个写法,相信经常写javascript都写腻了。e.target是事件对象,就是事件是由谁触发的,有的浏览器使用的是e.srcElement,但如今基本都遵循标准了的。e.currentTarget则是当前事件是由哪个dom元素去处理,相当于函数内的this。我们点击最内部的div,控制台输出的是:

addEventListener_result

点击中间,输出:

addEventListener_middle

可以清楚的看到,事件流确实是先被最外层捕获接着向内部的目标节点延伸再由目标节点向外冒泡,先捕获再冒泡!

事件代理

事件代理的原理就是事件捕获。比如给一个有很多行的table里边每个tr绑定一个点击事件,如果分别给每个tr绑,肯定不是一个好办法。正确做法是让table去代理,给table绑定捕获阶段的click事件,这样里边每个tr点击时都会被外层的table捕获到。

阻止冒泡

一般事件都是注册在冒泡阶段,而有时,出于性能考虑或者不想触发父节点上绑定的相同事件,我们需要阻止事件冒泡。比如上边的例子,我们给三个div都注册了冒泡阶段的click事件,我们想点击最内部的div时中间和最外边的div的click事件不要触发,就需要阻止冒泡。标准做法是使用e.stopPropagation(),而在老IE上,使用的是window.event.cancleBubble=true。

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.