众所周知(假装是这样的),在IE(支持冒泡)和NetScape(支持捕获)两者打完之后,W3C给了个折中的结果“先捕获再冒泡,就这么定了”。
所以事件的触发会经历三个阶段:
- 事件捕获阶段(
e.eventPhase==1
)从document开始,一层层往里面捕获,遇到捕获事件立即触发执行; - 处于目标阶段(
e.eventPhase==2
)到达事件位置,触发事件; - 事件冒泡阶段(
e.eventPhase==3
)从事件位置一层层往外冒泡,直到返回到document,遇到冒泡事件立即触发执行。
obj.addEventListener(“click”,func, true);
//捕获方式
obj.addEventListener(“click”,func, false);
//冒泡方式
再来看下event.stopPropagation()到底是个什么鬼。
说是阻止冒泡,然而并不严谨昂~,因为它不只是阻止冒泡,还阻止事件的继续捕获,说白了就是不让事件继续传播了呢。当然不传播不代表当前节点的其他事件也不执行,如果想要接下来绑定在该节点的其他事件也阻止的话,就要用event.stopImmediatePropagation()
来阻止(这么多额,真是烦躁)。
还有更烦躁的,捕获和冒泡的顺序是严格的,所以事件之间的执行顺序和事件注册的顺序是没关系的。
但是!如果一个节点既绑定了捕获事件,又绑定了冒泡事件,并且都处于eventPhase==2 && e.target==e.currentTarget
的阶段,那么就和注册顺序保持一致。这个时候就要看事件发生的位置和阶段了,不要忽略不要忽略不要忽略,点击一个div的时候最先进行的处理是判断e.target
和e.currentTarget
是否相等。
比如我按照这样的顺序写了代码:
<div id="p">
parent
<p id="c">child</p>
</div>
<script type="text/javascript">
window.alert = function(msg){
console.log(msg);
}
p.addEventListener('click', function (e) {
alert('父节点捕获:' + e.eventPhase);
alert('父节点捕获:' + e.currentTarget);
alert('父节点捕获:' + (e.currentTarget == e.target));
alert('父节点捕获');
}, true);
p.addEventListener('click', function (e) {
alert('父节点冒泡:' + e.eventPhase);
alert('父节点冒泡:' + e.currentTarget);
alert('父节点冒泡:' + (e.currentTarget == e.target));
alert('父节点冒泡');
}, false);
c.addEventListener('click', function (e) {
alert('子节点冒泡:' + e.eventPhase);
alert('子节点冒泡:' + e.currentTarget);
alert('子节点冒泡:' + (e.currentTarget == e.target));
alert('子节点冒泡');
}, false);
c.addEventListener('click', function (e) {
alert('子节点捕获:' + e.eventPhase);
alert('子节点捕获:' + e.currentTarget);
alert('子节点捕获:' + (e.currentTarget == e.target));
alert('子节点捕获');
}, true);
</script>
我点击child的时候,控制台输出的是什么呢。。。
所以点击child的时候父元素不处于phase2,所以父元素身上的事件就是最先捕获,最后冒泡。但对于child元素来说,event的currentTarget就是child本身,而我先注册了冒泡后注册的捕获,所以child就得乖乖先冒泡后捕获。。。
是不是觉得很晕的说。。。
further more 浏览器宝宝为我们提供了一个获取节点事件数的方法(如论是什么方式的绑定,addEventListener
也算,onclick=‘func()’
也算):getEventListeners(node)
,返回一个事件数组。
如果注册事件类型不一样,返回不同数组:
比如这里有三个click事件和一个
mousedown
事件。