JavaScript中事件冒泡与事件捕获

1.什么是事件冒泡

如图:现在有3个嵌套div,且都有onclick事件,当div_3被单击时,依次触发div_3=>div_2=>div_1的click事件。 

这就是事件冒泡:当一个事件被触发时,依次由最上层元素(div_3)向下遍历并执行该元素及父元素相同事件的过程就是事件冒泡。

2.什么是事件捕获

参照上文的图片,事件捕获是指: 当事件由最底层(div_1)向上遍历并执行时称为事件捕获。

3.为什么会有事件冒泡与事件捕获

如图,冒泡事件之所存在与js的事件处理机制有关,事件的触发过程是这样:

       i:某元素事件被触发,会找到其父元素及祖父元素直至根元素,并组成“树”

       ii:从“树”根元素向上寻找父元素,若父元素包含有该事件且注册事件的userCapture参数为true(这个属性下文会讲到)便触发

       iii:继续向上遍历寻找父元素的父元素,并进行相同的判断,直至正真触发事件的“顶层”元素(到此为止的过程便是事件捕获过程)

       iiii:到达“顶部”之后,再往下从“叶子”往“根”再次遍历(事件冒泡开始),若元素包含有该事件且注册事件的userCapture参数为false便触发

       v:不断遍历,到根部结束(事件冒泡结束)

这就产生了事件冒泡与事件捕获的概念。

3.实验

先看代码,我们加了3个div(运行结果与上文第一幅图相同):

<html>
<head>
<script>
function init(){
	document.getElementById("div_1").addEventListener("click",function (){alert(this.id);},true);
	document.getElementById("div_2").addEventListener("click",function (){alert(this.id);},true);
	document.getElementById("div_3").addEventListener("click",function (){alert(this.id);},true);

}
</script>
</head>
<body onload="init()">
<div  id="div_1" style="background-color:#FFF200; 150px;height:150px"  >    div_1 <br/>
	<div id="div_2" style="background-color:#EFE4B0; 120px;height:120px; margin-left:15px;"  >     div_2 <br/>
	<div id="div_3" style="background-color:#B5E61D; 90px;height:90px;  margin-left:15px; "  >        div_3<br/><br/>
		  click  me!	
	</div>
	</div>
 </div>
</body>
</html>


现在:单击div_3看看会怎样?

结果:依次弹出“div_1”=>“div_2”=>"div_3"

我们注意到这里用了一个函数addEventListener();他的作用是为元素注册事件,你可以将它理解为与οnclick=“xxx()” 类似,但是任有区别(区别参考这里

他有3个参数,分别是:eventType:表示事件类型

                                      function:触发事件将执行的函数

                                      userCapture:是否执行捕捉

前两个参数从字面就可以理解,但是第三个参数值什么意思?

所谓执行捕捉,是指在事件捕获的过程中需要触发的事件,若该参数为false则只会触发事件冒泡,若为true,则只会触发事件捕获。

为了验证,我们修改上面的init()函数,如下:

function init(){
	document.getElementById("div_1").addEventListener("click",function (){alert(this.id);},false);
	document.getElementById("div_2").addEventListener("click",function (){alert(this.id);},true);
	document.getElementById("div_3").addEventListener("click",function (){alert(this.id);},false);

}


将div_1与div_3的userCapture参数改为false。这样,按照我们上面的定义,这次单击div_3弹框顺序应该是:div_2=>div_3=>div_1.

经测试,确实如此  :)

4.对我们的好处及缺点

 
通过实验,我们已经对JS事件机制有了更深层次理解:
事件冒泡更准确的说是,一个元素事件被触发时遍历执行其“元素树”上所有非捕捉(usercapture=false)同类事件的过程。
事件捕获更准确的说是,一个元素事件被触发时遍历执行其“元素树”上所有捕捉(usercapture=false)同类事件的过程。
 
优点
 
因为这些特性,他有一些好处供我们利用:如,在页面上有一个大的div,在里面散布里大量元素,我只需给这个div加上click事件就能获知是谁触发了click,而不用每个元素都注册事件。
 
我们做个示例,修改了上面的html,只给div_1添注册了事件,getAimEle()获取当前事件源:
<html>
<head>
<script>
function init(){
	document.getElementById("div_1").addEventListener("click",function (){   
		var target =getAimEle();
		alert(target.id);
	},false);
}
function getAimEle(){
	var ele=window.event;
	return ele.target==null? ele.srcElement:ele.target;  
}
</script>
</head>
<body onload="init()">
<div  id="div_1" style="background-color:#FFF200; 150px;height:150px" >    div_1 <br/>
	<div id="div_2" style="background-color:#EFE4B0; 120px;height:120px; margin-left:15px;"   >     div_2 <br/>
	<div id="div_3" style="background-color:#B5E61D; 90px;height:90px;  margin-left:15px; "  >        div_3<br/><br/>
		  click  me!	
	</div>
	</div>
 </div>
</body>
</html>
这时候,单击div_3,会弹出"div_3",而我们并没有特意为他注册事件。
 
缺点
 
缺点也是显而易见的,当我不需要触发事件时执行父类相同事件方法的时候,冒泡就成了极大的缺陷,他会执行不该执行的代码,让运行速度减慢,而且导致的bug也不容易发现。
例如,如果你使用了mouseOver一类会频繁触发的事件,每次鼠标的移动会导致一次“元素树”的遍历,那性能会是很大问题
 

5.如何消除冒泡与捕获

 
正确使用冒泡非常重要,当需要“冒泡”的时候让他“冒”,不需要的时候,则应该及时阻止。
 
阻止有两种方式:cancelBubble=true(IE中使用,),stopPropagation()(FF及谷歌中使用)。注意:IE高版本实际上两者都开始支持
 
 
这时候,我们修改html,3个div都注册了单击事件,但是div_2处用了“阻止冒泡”:
<html>
<head>
<script>
function init(){
	document.getElementById("div_1").addEventListener("click",function (){alert(this.id);},false);
	document.getElementById("div_2").addEventListener("click",function (){
		alert(this.id);
		var e=window.event;
		if(e.cancelBubble!=null) e.cancelBubble=true;
		else e.stopPropagation();
	
	;},false);
	document.getElementById("div_3").addEventListener("click",function (){alert(this.id);},false);

}
</script>
</head>
<body onload="init()">
<div  id="div_1" style="background-color:#FFF200; 150px;height:150px" >    div_1 <br/>
	<div id="div_2" style="background-color:#EFE4B0; 120px;height:120px; margin-left:15px;"   >     div_2 <br/>
	<div id="div_3" style="background-color:#B5E61D; 90px;height:90px;  margin-left:15px; "  >        div_3<br/><br/>
		  click  me!	
	</div>
	</div>
 </div>
</body>
</html>

单击div_3,效果是:div_3=>div_2
 
由于div_2执行之后阻止了冒泡,div_1没有被执行
 
对于“捕获事件”(usercapture=true),上面的方法依然适用。 就是说,这种阻止冒泡方式实际上是限制了JS的事件机制,所以中断了“元素树”的遍历。
 
 
 
 

作者:Mr.Jimmy
出处:https://www.cnblogs.com/JHelius
联系:yanyangzhihuo@foxmail.com
如有疑问欢迎讨论,转载请注明出处

原文地址:https://www.cnblogs.com/JHelius/p/14318916.html