粗浅的总结下事件流
Posted 学点技术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了粗浅的总结下事件流相关的知识,希望对你有一定的参考价值。
什么是事件流?以及为什么要有事件流?
简单的说,事件流就是,确认触发条件满足时,事件对应函数的调用顺序。举个例子,鼠标光标在某个按钮上点击了,按钮又绑定了mousedown事件,那么其对应的函数就会调用。而其实,光标落下的位置也在document、window的范围内,或许还可能在其他元素的盒模型内。如果这些元素也都绑定了mousedown事件,那么哪个元素的mousedown事件对应的函数先调用呢?这就需要确认一个发生的顺序问题。
具体的说,事件流分为三个阶段,即捕获阶段、目标(处理)阶段、冒泡阶段。网上随便找了一张示意图,如下:
其实这张图以及网上很多说法(比如百度百科DOM事件流)都不是很严谨,漏掉了window对象。事件流应该是从window开始,在window对象结束(如果捕获阶段和冒泡阶段,window都绑定了事件的话)。估计是window对象通常只在冒泡阶段绑定load事件吧,不谈也没什么影响。
一个演示事件流的例子
下图中,在鼠标单击金黄色span区域时,控制台对应打印出日志。
源代码在这里:
1 <!DOCTYPE html> 2 <html id="html"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 <style> 7 div{ 8 width: 200px; 9 height: 200px; 10 font-size: 30px; 11 color: #fff; 12 background: green; 13 } 14 span{ 15 display: block; 16 width: 100px; 17 height: 100px; 18 background: goldenrod; 19 color: #fff; 20 font-size: 30px; 21 } 22 body{ 23 border: 1px solid #000; 24 } 25 </style> 26 <script> 27 28 window.onload=function(){ 29 var div=document.getElementById("div"); 30 var span=document.getElementById("span"); 31 var html=document.getElementById("html"); 32 var body=document.getElementById("body"); 33 var head=document.getElementById("head"); 34 35 body.addEventListener(\'click\',function(){console.log(\'body冒泡\')},false); 36 html.addEventListener(\'click\',function(){console.log(\'html冒泡\')},false); 37 window.addEventListener(\'click\',function(){console.log(\'window冒泡\')},false); 38 document.addEventListener(\'click\',function(){console.log(\'document冒泡\')},false); 39 div.addEventListener(\'click\',function(){console.log(\'div冒泡\')},false); 40 span.addEventListener(\'click\',function(){console.log(\'span冒泡\')},false); 41 span.addEventListener(\'click\',function(){console.log(\'span捕获\')},true); 42 document.addEventListener(\'click\',function(){console.log(\'document捕获\')},true); 43 window.addEventListener(\'click\',function(){console.log(\'window捕获\')},true); 44 html.addEventListener(\'click\',function(){console.log(\'html捕获\')},true); 45 body.addEventListener(\'click\',function(){console.log(\'body捕获\')},true); 46 div.addEventListener(\'click\',function(){console.log(\'div捕获\')},true); 47 }; 48 </script> 49 </head> 50 <body id="body"> 51 <div id="div">div 52 <span id="span">span</span> 53 </div> 54 </body> 55 </html>
结合图片和代码,可以发现两个问题:
1、代码中故意将打印带有“冒泡”字样的语句写在了前面,而且也没有按“window捕获->document捕获->html捕获->body捕获->div捕获->span捕获->span冒泡->div冒泡->body冒泡->html冒泡->document冒泡->window冒泡”这样的顺序,但是控制台中打印的日志基本上与事件流的顺序一致。
2、第二个问题,也就是控制台中打印的日志与事件流的顺序不一致的地方,为什么“span冒泡”在“span捕获”前面呢?因为,触发的是span绑定的事件,那么span绑定的事件就在目标阶段触发。触发顺按照代码中书写顺序来。本例中,即
span.addEventListener(\'click\',function(){console.log(\'span冒泡\')},false);
在
span.addEventListener(\'click\',function(){console.log(\'span捕获\')},true); 前面。
注:
addEventListener绑定事件语法: 对象.addEventListener(事件名称,事件绑定函数,布尔值)
布尔值取值说明:
* true 事件是在捕获的阶段发生的
* false 事件是在冒泡的阶段发生的(缺省值)
阻止事件流
说到事件流,一般都要说下阻止事件流。我们知道可以通过两种方法绑定事件,一种是用on,一种是上面提到的addEventListener。针对绑定事件的方式不同,阻止事件流的方法也不同。
情况1:
用on给元素绑定事件,所有的浏览器都是一样的,先触发事件源对象,然后再往外层元素冒泡。所以,只需要阻止冒泡就可以了。将event对象身上的cancelBubble的值设为true即可。
来看一个例子:
图中,点击box3后,box3、box2身上的事件会发生。然后,停止在box2,也就是不会在向外冒泡了。所以box1身上的事件没有发生。
关键代码如下:
1 box1.onclick=function(ev){ 2 console.log(\'box1点击了\'); 3 }; 4 box2.onclick=function(ev){ 5 console.log(\'box2点击了\'); 6 ev.cancelBubble=true; //这里阻止了冒泡 7 }; 8 box3.onclick=function(ev){ 9 console.log(\'box3点击了\'); 10 };
情况2:
用addEventListener绑定事件,可指定事件发生在捕获阶段还是在冒泡阶段。在事件函数内调用event身上的stopPropagation()方法,可以阻止事件流继续蔓延。
看一个具体的例子:
图中,因为在冒泡阶段box6绑定的事件函数内,阻止了事件流,点击box6,事件流会在冒泡阶段box6绑定的事件发生后停止。如果没有阻止事件流,那么控制台中将会依次打印:捕获阶段box4点击了->捕获阶段box5点击了->捕获阶段box6点击了->冒泡阶段box4点击了->冒泡阶段box5点击了->冒泡阶段box6点击了。
关键代码如下:
1 box4.addEventListener(\'click\',function(ev){ 2 console.log(\'捕获阶段box4点击了\'); 3 },true); 4 box5.addEventListener(\'click\',function(ev){ 5 console.log(\'捕获阶段box5点击了\'); 6 },true); 7 box6.addEventListener(\'click\',function(ev){ 8 console.log(\'捕获阶段box6点击了\'); 9 },true); 10 11 box4.addEventListener(\'click\',function(ev){ 12 console.log(\'冒泡阶段box4点击了\'); 13 }); 14 box5.addEventListener(\'click\',function(ev){ 15 console.log(\'冒泡阶段box5点击了\'); 16 }); 17 box6.addEventListener(\'click\',function(ev){ 18 console.log(\'冒泡阶段box6点击了\'); 19 ev.stopPropagation(); //因为在此处阻止了事件流蔓延,单击box6的时候,只打印\'冒泡阶段box6点击了\'。不会波及到box5和box4。 20 });
本作品采用知识共享署名 4.0 国际许可协议进行许可。
以上是关于粗浅的总结下事件流的主要内容,如果未能解决你的问题,请参考以下文章