冒泡事件和捕获事件

144 阅读3分钟

事件流就是指页面接收事件的顺序

即事件发生在元素节点之间按照特定的顺序传播,这个传播过程就是 DOM事件流

事件流分为三个阶段:

  1. 捕获阶段
  2. 当前目标阶段
  3. 冒泡阶段

事件冒泡:IE最早提出,事件开始时由 具体的元素 接收,然后逐级向上传播到DOM最顶层节点的过程

事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程

目标阶段:到达的具体元素

当我们给div注册一个点击事件时:即从上到下是捕获,从上到下是冒泡

1、捕获阶段和冒泡阶段

  1. JavaScript只能执行捕获 或 冒泡其中一个阶段
  2. onclick 和 attachEvent 只能得到冒泡阶段
  3. addEventListener(type, listener[, useCapture]) 第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在事件冒泡阶段调用事件处理程序
  4. 有的事件是没有冒泡的,如:onbluronfocusonmouseenteronmouseleave 等
  5. 实际开发中,我们很少用事件捕获,我们更关注事件冒泡
 <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、阻止事件冒泡

阻止事件冒泡: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() 就可以了

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'
 })