一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
DOM事件流
概述
事件流描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
比如我们给一个div注册了点击事件,触发事件时会经理如下过程:
DOM事件流3个阶段
DOM事件流的3个阶段分别是:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
整个流程:
当事件被触发时,首先经理的阶段是捕获阶段,是Document节点自上而下向目标节点传播的过程,所有经过的节点都会触发相对应的事件。
随后事件到了具体元素身上也就是目标阶段,在具体元素上发生,它会被看作是冒泡阶段的一部分
而冒泡阶段是从目标节点往上,是一个向Document节点传播的过程
一些注意事项
1 . JS代码中只能执行 捕获阶段或者冒泡阶段其中的一个阶段
2 . onclick 和 attachEvent(ie)只能得到冒泡阶段
3 . 捕获阶段 如果addEventListener第三个参数是true,则处于捕获阶段 document -> html -> body -> father -> son
案例
<div class="father">
<div class="son">son</div>
</div>
var son = document.querySelector('.son');
var father = document.querySelector('.father');
son.addEventListener('click', function () {
alert('son');
}, true);
因为当前捕获阶段只有son盒子绑定了事件,所以从document一直检索到son盒子时,只有son盒子会触发事件。
那如果这个案例的father也绑定了事件呢
father.addEventListener('click', function () {
alert('father');
}, true);
那么就会按照捕获顺序,先执行了father的事件再执行了son 的事件
4 . 冒泡阶段 如果addEventListener第三个参数是false或者没有参数,则处于冒泡阶段 son -> father -> body ->html -> document
将上面事件监听方法中的参数true删掉或者换成false,就是冒泡阶段了,执行顺序是先从son开始检索一直到document,所以会先触发son 再触发father
阻止事件冒泡
实际开发中我们很少使用事件捕获,我们更关注事件冒泡。 而有些事件是没有冒泡的,比如onblur、onfocus、onmouseleave。由于事件冒泡本身的特性,会带来坏处也会带来好处,所以需要我们灵活掌握。
见如下场景:
有时候我们执行完具体对象就不想让它再向上冒泡传播了,不然会执行其他也有绑定事件的对象,所以我们需要阻止冒泡。阻止冒泡 dom推荐的标准 stopPropagation()
案例:
<div class="father">
<div class="son">son</div>
</div>
var son = document.querySelector('.son');
son.addEventListener('click', function (e) {
alert('son');
e.stopPropagation();
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function () {
alert('father');
}, false);
document.addEventListener('click', function () {
alert('document');
}, false);
注意: 这个案例中,我们只给了son阻止了冒泡,并没有给father也阻止冒泡,所以点击father的时候它还是会向上冒泡。
同样的,stopPropagation()方法也有兼容性问题,ie678不支持,在ie678中只能使用事件对象的cancelBubble属性!
下面是一个兼容性方案:
if (e && e.stopPropagation) { // 逻辑中断
e.stopPropagation();
} else {
window.event.cancelBubble = true;
}