zoukankan      html  css  js  c++  java
  • 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浏览器中安装以及使用Elasticsearch head 插件
    windows10 升级并安装配置 jmeter5.3
    linux下部署Elasticsearch6.8.1版本的集群
    【Rollo的Python之路】Python 爬虫系统学习 (八) logging模块的使用
    【Rollo的Python之路】Python 爬虫系统学习 (七) Scrapy初识
    【Rollo的Python之路】Python 爬虫系统学习 (六) Selenium 模拟登录
    【Rollo的Python之路】Python 爬虫系统学习 (五) Selenium
    【Rollo的Python之路】Python 爬虫系统学习 (四) XPath学习
    【Rollo的Python之路】Python 爬虫系统学习 (三)
    【Rollo的Python之路】Python sys argv[] 函数用法笔记
  • 原文地址:https://www.cnblogs.com/JHelius/p/14318916.html
Copyright © 2011-2022 走看看