当使用事件捕获时javascript
| | ---------------| |----------------- | outer | | | | -----------| |----------- | | |inner \ / | | | ------------------------- | | Event CAPTURING | -----------------------------------
outer
上的事件处理器先触发, 而后是inner
上的java
/ \
---------------| |----------------- | outer | | | | -----------| |----------- | | |inner | | | | | ------------------------- | | Event BUBBLING | -----------------------------------
与事件捕获相反, 当使用事件冒泡时, inner上的事件处理器先被触发, 其后是outer上面的。node
通常来讲事件冒泡机制,用的更多一些,因此在IE8以及以前,IE只支持事件冒泡。IE9+/FF/Chrome这2种模型都支持,能够经过addEventListener((type, listener, useCapture)的useCapture来设定,useCapture=false表明着事件冒泡,useCapture=true表明着采用事件捕获。默认是false,即事件冒泡。app
DOM事件流函数
DOM事件流:将事件分为三个阶段:捕获阶段、目标阶段、冒泡阶段。先调用捕获阶段的处理函数,其次调用目标阶段的处理函数,最后调用冒泡阶段的处理函数。ui
咱们在outC上触发onclick事件(这个是目标对象),若是咱们在outC上同时绑定捕获阶段/冒泡阶段事件处理函数会怎么样呢?this
<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事件流中的目标阶段。目标阶段函数的执行顺序:先注册的先执行,后注册的后执行。这就是上面咱们说的,在目标对象上绑定的函数是采用捕获,仍是采用冒泡,都没有什么关系,由于冒泡和捕获只是对父元素上的函数执行顺序有影响,对本身没有什么影响。若是不信,能够将下面的代码放进去验证。spa
// 目标(自身触发事件,是冒泡仍是捕获无所谓) outC.addEventListener('click',function(){alert("target1");},false); outC.addEventListener('click',function(){alert("target2");},true); outC.addEventListener('click',function(){alert("target3");},true); outC.addEventListener('click',function(){alert("target4");},false);
至此咱们能够给出事件函数执行顺序的结论了:捕获阶段的处理函数最早执行,其次是目标阶段的处理函数,最后是冒泡阶段的处理函数。目标阶段的处理函数,先注册的先执行,后注册的后执行。.net
1. 阻止冒泡事件代理
主要是用于阻止事件传播。阻止它被分派到其余的DOM节点上,在事件传播的任何阶段都能使用。使用方法以下(兼容IE):
function stopBubble(event){ if(window.event){//兼容IE window.event.cancelBubble=true; }else{ event.stopPropagation(); }
2. 阻止默认事件
像submit这类的表单元素,都会绑定默认事件,若是不阻止默认事件,则绑定的其余方法也会无效。使用方法以下(兼容IE):
function stopDefaultEvent(event){ if(window.event){//兼容IE window.event.returnValue=false; }else{ event.preventDefault() } return false; }
上面的例子是要阻止冒泡,有时候冒泡机制也能够被利用。先看一个问题:
假设要在页面上放在 10^n(n>=1) 个列表项元素,当我点击某个元素时,须要输出点击的是第几个。
通常作法是,遍历时给每一个元素绑定点击事件:
var li = document.getElementsByTagName('li'); for(var i=0; i<li.length; i++){ li[i].setAttribute('i',i+1); li[i].addEventListener('click', function(e){ var b = this.getAttribute('i'); console.log('这是第' + b + '个<li>元素'); }); }
另外一种方法是,能够利用冒泡机制,在父元素只绑定一次点击事件:
var li = document.getElementsByTagName('li'); for(var i=0; i<li.length; i++){ li[i].setAttribute('i',i+1); } var ul = document.getElementById('wrapper'); ul.addEventListener('click', function(e){ if(e.target && e.target.nodeName.toUpperCase() === 'LI'){ var b = e.target.getAttribute('i'); console.log('这是第' + b + '个<li>元素'); } });
这种事件委托的方式减小了事件处理程序,也能下降程序的复杂性和出错几率。
参考:
https://www.jb51.net/article/94394.htm
https://blog.csdn.net/michael8512/article/details/77447537