谈起JavaScript的 事件,事件冒泡、事件捕获、阻止默认事件这三个话题,不管是面试仍是在平时的工做中,都很难避免。javascript
DOM事件标准定义了两种事件流,这两种事件流有着显著的不一样而且可能对你的应用有着至关大的影响。这两种事件流分别是捕获和冒泡。和许多Web技术同样,在它们成为标准以前,Netscape和微软各自不一样地实现了它们。Netscape选择实现了捕获事件流,微软则实现了冒泡事件流。幸运的是,W3C决定组合使用这两种方法,而且大多数新浏览器都遵循这两种事件流方式。html
概念:java
事件冒泡:事件最开始由最具体的元素接收(文档层次中嵌套最深的那个节点),而后逐级向上传播最不具体的节点(document)。面试
事件捕获:事件由最不具体的元素接收,最后传播至最具体的元素。浏览器
事件冒泡代码实例:安全
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>事件冒泡</title> </head> <body> <div id="e"> <input id="btn" type="button" value="按钮"> </div> <script type="text/javascript"> var e = document.getElementById('e'); var btn = document.getElementById('btn'); e.onclick = function (){ alert("你点击了div"); }; btn.onclick = function(){ alert("你点击了button"); } </script> </body> </html>
当我点击了按钮时候,运行 --你点击了button ——你点击了div,这就是事件冒泡。函数
事件捕获代码:url
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>事件冒泡</title> </head> <body> <div id="e"> <input id="btn" type="button" value="按钮"> </div> <script type="text/javascript"> var e = document.getElementById('e'); var btn = document.getElementById('btn'); e.addEventListener('click',function(){ alert('你点击了div'); },true); btn.addEventListener('click',function(){ alert('你点击了button'); },true); </script> </body> </html>
当我点击了按钮时候,运行 --你点击了div ——你点击了button,这就是事件捕获。spa
(1)冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。.net
IE 5.5: input -> div -> body -> document
IE 6.0: input -> div -> body -> html -> document
Mozilla 1.0: input -> div -> body -> html -> document -> window
事件冒泡见下图:
(2)捕获型事件(event capturing):事件从最不精确的对象(document 对象)开始触发,而后到最精确(也能够在窗口级别捕获事件,不过必须由开发人员特别指定)。
事件捕获见下图:
(3)DOM事件流:同时支持两种事件模型:捕获型事件和冒泡型事件,可是,捕获型事件先发生。两种事件流会触及DOM中的全部对象,从document对象开始,也在document对象结束。
通常来讲事件冒泡机制,用的更多一些,因此在IE8以及以前,IE只支持事件冒泡。IE9+/FF/Chrome这2种模型都支持,能够经过addEventListener((type, listener, useCapture)的useCapture来设定,useCapture=false表明着事件冒泡,useCapture=true表明着采用事件捕获。而不兼容W3C的浏览器(IE)用attachEvent()方法,此方法没有相关设置,不过IE的事件模型默认是在事件冒泡时执行的,也就是在useCapture等于false的时候执行,因此把在处理事件时把useCapture设置为false是比较安全,也实现兼容浏览器的效果。
DOM事件流:将事件分为三个阶段:捕获阶段、目标阶段、冒泡阶段。先调用捕获阶段的处理函数,其次调用目标阶段的处理函数,最后调用冒泡阶段的处理函数。
<script> window.onload = function(){ var outA = document.getElementById("outA"); var outB = document.getElementById("outB"); var outC = document.getElementById("outC"); // 目标(自身触发事件,是冒泡仍是捕获无所谓) outC.addEventListener('click',function(){alert("target");},true); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble1");},false); outB.addEventListener('click',function(){alert("bubble2");},false); // 事件捕获 outA.addEventListener('click',function(){alert("capture1");},true); outB.addEventListener('click',function(){alert("capture2");},true); }; </script> <body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> </div> </div> </body>
当点击outC的时候,依次打印出capture1-->capture2-->target-->bubble2-->bubble1。到这里是否是能够理解addEventListener(type,handler,useCapture)这个API中第三个参数useCapture的含义呢?useCapture=false意味着:将事件处理函数加入到冒泡阶段,在冒泡阶段会被调用;useCapture=true意味着:将事件处理函数加入到捕获阶段,在捕获阶段会被调用。从DOM事件流模型能够看出,捕获阶段的事件处理函数,必定比冒泡阶段的事件处理函数先执行。
<script> window.onload = function(){ var outA = document.getElementById("outA"); var outB = document.getElementById("outB"); var outC = document.getElementById("outC"); // 目标(自身触发事件,是冒泡仍是捕获无所谓) outC.addEventListener('click',function(){alert("target2");},true); outC.addEventListener('click',function(){alert("target1");},true); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble1");},false); outB.addEventListener('click',function(){alert("bubble2");},false); // 事件捕获 outA.addEventListener('click',function(){alert("capture1");},true); outB.addEventListener('click',function(){alert("capture2");},true); }; </script> <body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> </div> </div> </body>
点击outC的时候,打印顺序是:capture1-->capture2-->target2-->target1-->bubble2-->bubble1。因为outC是咱们触发事件的目标对象,在outC上注册的事件处理函数,属于DOM事件流中的目标阶段。目标阶段函数的执行顺序:先注册的先执行,后注册的后执行。这就是上面咱们说的,在目标对象上绑定的函数是采用捕获,仍是采用冒泡,都没有什么关系,由于冒泡和捕获只是对父元素上的函数执行顺序有影响,对本身没有什么影响。若是不信,能够将下面的代码放进去验证。
此咱们能够给出事件函数执行顺序的结论了:捕获阶段的处理函数最早执行,其次是目标阶段的处理函数,最后是冒泡阶段的处理函数。目标阶段的处理函数,先注册的先执行,后注册的后执行。
默认状况下,多个事件处理函数会按照DOM事件流模型中的顺序执行。若是子元素上发生某个事件,不须要执行父元素上注册的事件处理函数,那么咱们能够中止捕获和冒泡,避免没有意义的函数调用。前面提到的5种事件绑定方式,均可以实现阻止事件的传播。IE8以及之前可过 window.event.cancelBubble=true阻止事件的继续传播;IE9+/FF/Chrome经过event.stopPropagation()阻止事件的继续传播。
<script> window.onload = function(){ var outA = document.getElementById("outA"); var outB = document.getElementById("outB"); var outC = document.getElementById("outC"); // 目标 outC.addEventListener('click',function(event){ alert("target"); if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true;//IE阻止事件冒泡,true表明阻止 } },false); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble");},false); // 事件捕获 outA.addEventListener('click',function(){alert("capture");},true); }; </script> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> </div> </div>
当点击outC的时候,以后打印出capture-->target,不会打印出bubble。由于当事件传播到outC上的处理函数时,经过stopPropagation阻止了事件的继续传播,因此不会继续传播到冒泡阶段。
原文连接:http://lib.csdn.net/article/javascript/6265?knId=505