DOM事件机制,事件捕获与事件冒泡先后执行顺序

DOM事件机制,事件捕获与事件冒泡先后执行顺序

DOM事件流的三个阶段

流的概念,在现今的JavaScript中随处可见。比如说React中的单向数据流,Node中的流,又或是今天本文所讲的DOM事件流,都是流的一种生动体现。用术语说流是对输入输出设备的抽象。以程序的角度说,流是具有方向的数据。事件流所描述的就是从页面中接受事件的顺序。事件流也有两种,分别是事件冒泡和事件捕获。

在所有的现代浏览器当中——除了IE9之前的版本——都实现了DOM2标准事件模型,即事件处理过程的三个阶段:捕获,目标,冒泡;在这里插入图片描述

  1. 捕获阶段

    ​ 先由文档的根节点document往事件触发对象,从外向内捕获事件对象;当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。(所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回 Window。)在目标元素对象本身上注册的捕获事件处理程序不会被调用。

  2. 目标阶段

    ​ 到达目标事件位置(事发地),触发事件;当事件不断的传递直到目标节点的时候,最终在目标节点上触发这个事件,就是目标阶段。

  3. 冒泡阶段

    ​ 再从目标事件位置往文档的根节点方向回溯,从内向外冒泡事件对象;事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点(我们平时用的事件绑定就是利用的事件冒泡的原理)。

    事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点。

    事件捕获的概念,与事件冒泡正好相反。它认为当某个事件发生时,父元素应该更早接收到事件,具体元素则最后接收到事件

    事件一开始从文档的根节点流向目标对象(捕获阶段),然后在目标对向上被触发(目标阶段),之后再回溯到文档的根节点(冒泡阶段)。

    实例代码:

    HTML:

    <!DOCTYPE html>
    <meta name="robots" content="noindex">
    <html>
    <head>
      <meta charset="utf-8">
      <title>JS Bin</title>
    </head>
    <body>
    <div class="level1 x">
      <div class="level2 x">
        <div class="level3 x">
          <div class="level4 x">
            <div class="level5 x">
              <div class="level6 x">
                <div class="level7 x">
                  
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    </body>
    </html>
    

    CSS:

    <style id="jsbin-css">
    * {
      box-sizing: border-box;
    }
    div[class^=level] {
      border: 1px solid;
      border-radius: 50%;
      display: inline-flex;
    }
    .level1 {
      padding: 10px;
      background: purple;
    }
    .level2 {
      padding: 10px;
      background: blue;
    }
    .level3 {
      padding: 10px;
      background: cyan;
    }
    .level4 {
      padding: 10px;
      background: green;
    }
    .level5 {
      padding: 10px;
      background: yellow;
    }
    .level6 {
      padding: 10px;
      background: orange;
    }
    .level7 {
      width: 50px;
      height: 50px;
      border: 1px solid;
      background: red;
      border-radius: 50%;
    }
    .x{
      background: transparent;
    }
    </style>
    

    JS:

    <script id="jsbin-javascript">
    const level1 = document.querySelector('.level1')
    const level2 = document.querySelector('.level2')
    const level3 = document.querySelector('.level3')
    const level4 = document.querySelector('.level4')
    const level5 = document.querySelector('.level5')
    const level6 = document.querySelector('.level6')
    const level7 = document.querySelector('.level7')
    
    let n = 1
    //注意e对象被传给所有监听函数
    //事件结束后,e对象就不存在了
    //所以我们先用一个变量保存e
    const bubblingPhase = e=>{
      const t = e.currentTarget
      setTimeout(()=>{
        t.classList.add('x')
      },n*1000)
      console.log('w');
      n+=1
    }
    const capturePhase = e=>{
      const t = e.currentTarget
      setTimeout(()=>{
        t.classList.remove('x')
      },n*1000)
      console.log('1');
      n+=1
    }
    
    level1.addEventListener('click',capturePhase,true)
    level1.addEventListener('click',bubblingPhase)
    level2.addEventListener('click', capturePhase,true)
    level2.addEventListener('click', bubblingPhase)
    level3.addEventListener('click', capturePhase,true)
    level3.addEventListener('click', bubblingPhase)
    level4.addEventListener('click', capturePhase,true)
    level4.addEventListener('click', bubblingPhase)
    level5.addEventListener('click', capturePhase,true)
    level5.addEventListener('click', bubblingPhase)
    level6.addEventListener('click', capturePhase,true)
    level6.addEventListener('click', bubblingPhase)
    level7.addEventListener('click', capturePhase,true)
    level7.addEventListener('click', bubblingPhase)
    </script>
    

    运行代码:

    当点击最里面(level7)的元素时,所有元素会先依次执行capturePhase(),再依次执行bubblingPhase(),可看到颜色变化.

    特例:只有一个div被监听时(不考虑父子同时被监听),capturePhase()和bubblingPhase()分别在捕获阶段和冒泡阶段监听click事件,用户点击的元素就是开发者监听的.

    addEventListener()第三个参数, 如果不传或为false就让fn走冒泡,如果为true,就让fn走捕获

    stopPropagation() 方法

    事件冒泡过程,是可以被阻止的。防止事件冒泡而带来不必要的错误和困扰。

    终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。

    一般用来封装某些独立的组件.

    有些事件不可取消冒泡:

    • MDN搜索 scroll event ,看到Bubbles和Cancelable

    • Bubbles的意思是该事件是否冒泡

    • Cancelable的意思是开发者是否可以取消冒泡

    • 阻止 scroll 滚动事件:

      • 要阻止滚动,可阻止wheel和touchstart的默认动作

        //阻止scroll默认动作没用,因先有滚动才有滚动事件
        
        //鼠标滚动事件
        api.addEventListener("wheel",(e)=>{
            e.preventDefault();
        })
        //手机触摸事件
        api.addEventListener("touchstart",(e)=>{
            e.preventDefault();
        })
        
        
      • 但是滚动条还能用,可用CSS让滚动条width: 0

        /*注意你需要找准滚动条所在的元素*/
        ::-webkit-scrollbar{
           width: 0;
        }
        
    DOM事件中targetcurrentTarget的区别
    • target是事件触发的真实元素(用户操作)
    • currentTarget是事件绑定的元素(开发者监听)
    • 事件处理函数中的this指向是中为currentTarget
    • currentTargettarget,有时候是同一个元素,有时候不是同一个元素 (因为事件冒泡)
      • 当事件是子元素触发时,currentTarget为绑定事件的元素,target为子元素
      • 当事件是元素自身触发时,currentTargettarget为同一个元素。

绑定的元素(开发者监听)

  • 事件处理函数中的this指向是中为currentTarget
  • currentTargettarget,有时候是同一个元素,有时候不是同一个元素 (因为事件冒泡)
    • 当事件是子元素触发时,currentTarget为绑定事件的元素,target为子元素
    • 当事件是元素自身触发时,currentTargettarget为同一个元素。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值