• 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
    如有疑问欢迎讨论,转载请注明出处

  • 相关阅读:
    Chrome 29 新功能一览
    7 款免费的 Metro UI 模板
    JPG渐进 & PNG/PNG24 交错测试
    你的钱,以后是放银行还是放支付宝?
    Bise IE6 在你的网站上加上它让IE滚蛋吧
    单例模式常见场景
    10 个最新的开发者工具
    大流量网站的底层系统架构
    DNS解析全过程及原理
    IE条件注释详解
  • 原文地址:https://www.cnblogs.com/JHelius/p/14318916.html
Copyright © 2020-2023  润新知