刚接触 JS 的那个时候,啥也不懂,只想着如何利用 Google、百度到的函数来解决实际的问题,不会想到去一探究竟。javascript
渐渐的,对 JS 的语言的不断深刻,有机会去了解一些原理性东西。最近在看 JQuery 源码,感触不少,总想着用原生的 JS 去实现本身的一个 JQuery 库。说实在的,JQuery 里面不少函数和思路,是千百开源工做者长期的贡献,哪能是短期就能消化的了。html
最近再次碰到 addEventListener
函数(MDN 上关于 addEventListener 的介绍,很详细),因为以前并无弄懂第三个参数的含义,要么默认值,要么手动设置成 false。此次看了很多文章,完全把事件冒泡和捕获弄懂。java
事件冒泡与捕获是 DOM 中事件传播的两种方式,好比说对于注册了相同事件的两个 DOM 元素(简单点就是两个 div,一里一外),当点击里层 div 的时候,这两个事件谁先执行。程序员
冒泡事件,由里向外,最里层的元素先执行,而后冒泡到外层。浏览器
捕获事件,由外向里,最外层的元素先执行,而后传递到内部。dom
在 IE 9 以前是只支持事件冒泡,IE 9(包括 IE 9) 以后和目前主流的浏览器都同时支持两种事件。编辑器
如何设置,只需修改 addEventListener
的第三个参数,true 为捕获,false 为冒泡,默认为冒泡。函数
举个简单的例子,spa
<div> <span class="out"> <span class="in"></span> </span> </div> <script type="text/javascript"> var dom_out = document.getElementsByClassName('out')[0]; var dom_in = document.getElementsByClassName('in')[0]; dom_out.addEventListener('click',function(){ alert('out'); },false); dom_in.addEventListener('click',function(){ alert('in'); },false); </script>
在上面这个例子中,事件是按照冒泡来执行的,点击里层的 in
,会看到先 alert
的顺序是先 "in" 后 "out",若是把事件改为捕获,alert
的顺序又不同了。code
<script type="text/javascript"> var dom_out = document.getElementsByClassName('out')[0]; var dom_in = document.getElementsByClassName('in')[0]; dom_out.addEventListener('click',function(){ alert('out'); },true); dom_in.addEventListener('click',function(){ alert('in'); },true); </script>
上面这个例子是捕获事件的例子,点击 in
效果是否是不同呢?
之因此会有冒泡和捕获事件(像 IE 9 以前的浏览器不支持捕获事件,还真是反程序员),毕竟在实际中处理事情确定有个前后顺序,要么由里向外,要么由外向里,二者都是必须的。
但有时候为了兼容 IE 9 如下版本的浏览器,都会把第三个参数设置成 false 或者默认(默认就是 false)。
如今已经说清楚冒泡和捕获,那么若是同时出现冒泡和捕获会出现什么结果?
原来浏览器处理时间分为两个阶段,捕获阶段和冒泡阶段,
先执行捕获阶段,若是事件是在捕获阶段执行的(true 状况),则执行;
而后是冒泡阶段,若是事件是在冒泡阶段执行的(false 状况),则执行;
来看一看例子就知道了:
<div> <span class="s1">s1 <span class="s2">s2 <span class="s3">s3 </span> </span> </span> </div>
此次咱们设置三个 span,分别是 s1, s2, s3,而后设置 s1,s3 为冒泡执行,s2 为捕获执行:
<script type="text/javascript"> var s1 = document.getElementsByClassName('s1')[0]; var s2 = document.getElementsByClassName('s2')[0]; var s3 = document.getElementsByClassName('s3')[0]; s1.addEventListener('click',function(){ alert('s1'); },false); s2.addEventListener('click',function(){ alert('s2'); },true); s3.addEventListener('click',function(){ alert('s3'); },false); </script>
从运行的效果来看,点击 s3,依次 alert
s2 => s3 => s1,说明:
捕获事件和冒泡事件同时存在的,并且捕获事件先执行,冒泡事件后执行;
若是元素存在事件且事件的执行时间与当前逻辑一致(冒泡或捕获),则执行。
固然,有时候咱们只想执行最内层或最外层的事件,根据内外层关系来把范围更广的事件取消掉(对于新手来讲,不取消冒泡,很容易中招的出现 bug)。event.stopPropagation()
(IE 中window.event.cancelBubble = true
)能够用来取消事件冒泡。
有时候对于浏览器的默认事件也须要取消,这时候用到的函数则是 event.preventDefault()
(IE 中window.event.returnValue = false
)。
那么默认事件取消和中止冒泡有什么区别呢?个人理解:浏览器的默认事件是指浏览器本身的事件(这不废话吗),好比 a 标签
的点击,表单的提交等,取消掉就不会执行啦;冒泡则取消的是由外向里(捕获)、由里向外(冒泡),stop 以后,就不会继续遍历了。stackoverflow 上的解答
看下例子,依旧是上面那个例子,不过每一个函数都加了 中止冒泡:
s1.addEventListener('click',function(e){ e.stopPropagation(); alert('s1'); },false); s2.addEventListener('click',function(e){ e.stopPropagation(); alert('s2'); },true); s3.addEventListener('click',function(e){ e.stopPropagation(); alert('s3'); },false);
点击的结果是:当点击 s2 或 s3 的时候,都会 alert
s2,点击 s1,弹出 s1。由于事件被取消的缘故,点击 s3,执行 s2后就不会在向下执行了。
在看一个 preventDefault
的例子。
<div> <a href="/">点我回主页</a> </div> <div> <a href="/" class="back">点我不回主页</a> </div> <script type="text/javascript"> var back = document.getElementsByClassName('back')[0]; back.addEventListener('click', function(e){ e.preventDefault(); }); </script>
第二个连接是否是回不了主页,由于浏览器的默认事件被取消了。
以上全部例子请在非低版本 IE 浏览器的环境下浏览 O_o
总结就补充两个兼容 IE 的函数吧:
function stopBubble(e) { //若是提供了事件对象,则这是一个非IE浏览器 if ( e && e.stopPropagation ) //所以它支持W3C的stopPropagation()方法 e.stopPropagation(); else //不然,咱们须要使用IE的方式来取消事件冒泡 window.event.cancelBubble = true; } //阻止浏览器的默认行为 function stopDefault( e ) { //阻止默认浏览器动做(W3C) if ( e && e.preventDefault ) e.preventDefault(); //IE中阻止函数器默认动做的方式 else window.event.returnValue = false; return false; }
共勉!
stackoverflow 什么是事件冒泡和捕捉
stackoverflow stopPropagation 和 preventDefault 的区别
MDN addEventListener
javascript阻止事件冒泡和浏览器的默认行为
因为 SF 编辑器不支持上传 demo,只能以图片展现,欢迎来 个人博客 查看 demo。