1、事件流
事件流就是指页面接收事件的顺序,
即事件发生在元素节点之间按照特定的顺序传播,这个传播过程就是 DOM事件流
事件流分为三个阶段:
- 捕获阶段
- 当前目标阶段
- 冒泡阶段
事件冒泡:IE最早提出,事件开始时由 具体的元素 接收,然后逐级向上传播到DOM最顶层节点的过程
事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程
目标阶段:到达的具体元素
当我们给div注册一个点击事件时:
1.1 捕获阶段和冒泡阶段
- JavaScript只能执行捕获 或 冒泡其中一个阶段
onclick和attachEvent只能得到冒泡阶段addEventListener(type, listener[, useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在事件冒泡阶段调用事件处理程序- 有的事件是没有冒泡的,如:
onblur、onfocus、onmouseenter、onmouseleave等 - 实际开发中,我们很少用事件捕获,我们更关注事件冒泡
<div class="parent">
<div class="son"></div>
</div>
let parent = document.querySelector('.parent')
let son = document.querySelector('.son');
捕获阶段:
// 当参数是true,处于捕获阶段,即从上往下, 先 document -> html -> body -> father -> son
// 当点击son时,先弹出father,再弹出 son
son.addEventListener('click', function() {
alert('son')
}, true)
parent.addEventListener('click', function() {
alert('father')
}, true)
冒泡阶段:
// 当参数是false或不写,处于冒泡阶段,即从上往下, 先son -> father -> body -> html -> document
// 当点击son时,先弹出son,再弹出 father
son.addEventListener('click', function() {
alert('son')
}, false)
parent.addEventListener('click', function() {
alert('father')
}, false)
2.2 阻止事件冒泡
阻止事件冒泡:e.stopPropagation()
使用方法很简单,你想要从哪个节点开始阻止事件冒泡,就把该语句放到哪个事件处理程序里面
e.stopPropagation();添加在 son元素 上面,当点击son时,其以上的父元素就都不会冒出来;- 如果
father上面也有父元素且不想让他们冒出来,同样需要在father上添加e.stopPropagation();
<div class="parent">
<a href="https://www.baidu.com"></a>
</div>
let parent = document.querySelector('.parent')
let baidu = document.querySelector('a');
// 当我们点击baidu时,直接跳转到百度首页,而不会弹出father
baidu.addEventListener('click', function(e) {
e.stopPropagation()
})
parent.addEventListener('click', function() {
alert('father')
})
但是注意了,
e.stopPropagation()有兼容性问题- 低版本浏览器使用:
window.event.cancelBubble = true,非标准
需要考虑兼容性问题时,可以做个判断:
if (e && e.stopPropagation()) {
e.stopPropagation()
} else {
window.event.cancelBubble = true
}
现在一般直接使用 e.stopPropagation() 就可以了
2.3 事件委托
在多数时候,冒泡事件确实会给我们带来麻烦,我们都会选择阻止事件冒泡,但是,冒泡事件也并不是一无是处
当我们点击下面的li 时,希望给它添加上一个样式,以前我们是用for找到该li,为每一个li都绑定一个点击事件,非常繁琐,而且访问DOM的次数越多,这就会延长整个页面的交互就绪时间
<ul>
<li>爱真的需要勇气1</li>
<li>爱真的需要勇气2</li>
<li>爱真的需要勇气3</li>
<li>爱真的需要勇气4</li>
<li>爱真的需要勇气5</li>
</ul>
现在我们可以利用事件委托来简化这个过程
事件委托也称为事件代理,在 jQuery 里面称为事件委派
事件委托原理
不是每一个子节点都单独设置事件监听器,而是把事件监听器设置在其父节点上,然后利用冒泡原理影响每一个子节点
所以,上面例子我们可以给ul注册一个点击事件,然后利用事件对象的target来找到当前点击的 li,事件会冒泡到li的父节点ul上,ul有注册事件,就会触发事件监听器
事件委托的作用:我们只操作了一次DOM,提高了程序的性能
let ul = document.querySelector('ul')
let lis = document.querySelectorAll('li');
ul.addEventListener('click', function(e) {
// e.target 找到当前点击的对象li
// 排他思想
for (let i = 0; i < lis.length; i++) {
lis[i].style.color = ''
}
// 为其添加样式
e.target.style.color = 'red'
})