事件流

什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM 2级事件流包括下面几个阶段。
(1) 事件捕获阶段
(2) 处于目标阶段
(3) 事件冒泡阶段

如何让事件先冒泡后捕获:
在 DOM 标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后再执行捕获之间。

一、addEventListener
addEventListener 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

addEventListener 事件目标可以是文档上的元素 Element、Document 和 Window 或者任何其他支持事件的对象(例如 XMLHttpRequest)。

参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener

语法:

 target.addEventListener(type, listener, options/useCapture) 
  1. type:表示监听事件类型的字符串

  2. listener:所监听的事件触发,会接受一个事件通知对象。

  3. options:一个指定有关 listener 属性的可选参数对象。可选值有 capture(事件捕获阶段传播到这里触发)、once(在 listener 添加之后最多值调用一次)、passive(设置为 true 时表示listener 永远不会调用 preventDefault())。

  4. useCapture:在 DOM 树中,注册了 listener 的元素,是否要先于它下面的 EventTarget 调用该 listener。

注:addEventListener 的第三个参数涉及到冒泡和捕获,为 true 时是捕获,为 false 时是冒泡。
或者是一个对象 { passive: true },针对的是 Safari 浏览器,禁止/开启使用滚动的时候要用到

示例:

<table id="outside">
    <tr>
      <td id="t1">one</td>
    </tr>
    <tr>
      <td id="t2">two</td>
    </tr>
  </table>

  <script>
    // 这个示例简单实现了点击 two 切换到 four,点击 four 再切换到 two 的效果。
    (function () {
      // 添加函数
      const modifytext = (text) => {
        const t2 = document.querySelector('#t2');
        if (t2.firstChild.nodeValue === text) {
          t2.firstChild.nodeValue = 'two';
        } else {
          t2.firstChild.nodeValue = text;
        }
      }

     // 给table添加事件监听器
      const element = document.querySelector('#outside');
      element.addEventListener('click', function () {
        modifytext('four')
      }, false)
    })()
  </script> 

二、原理
事件捕获和事件冒泡分别是 网景(Netscape)和 IE 对 DOM 事件产生顺序的描述。

网景 认为 DOM 接收的事件应该最先是 window,然后到 document,接着一层一层往下,最后才到具体的元素接收到事件,即 事件捕获。

IE 则认为 DOM 事件应该是具体元素先接收到,然后再一层一层往上,接着到 document,最后才到 window,即 事件冒泡。

最后 W3C 对这两种方案进行了统一:将 DOM 事件分为两个阶段,事件捕获和事件冒泡阶段。

当一个元素被点击,首先是事件捕获阶段,window 最先接收事件,然后一层一层往下捕获,最后由具体元素接收;之后再由具体元素再一层一层往上冒泡,到 window 接收事件。

所以:

事件冒泡:当给某个目标元素绑定了事件之后,这个事件会依次在它的父级元素中被触发(当然前提是这个父级元素也有这个同名称的事件,比如子元素和父元素都绑定了 click 事件就触发父元素的 click)。

事件捕获:和冒泡相反,会从上层传递到下层。

三、案例

<ul class="ul">
    <li class="li">
      <button class="btn">点我</button>
    </li>
  </ul>

  <script>
    window.onload = function () {
      const btn = document.querySelector('.btn');
      btn.addEventListener('click', function () {
        console.log('button');
      })

      const li = document.querySelector('.li');
      li.addEventListener('click', function () {
        console.log('li');
      })

      const ul = document.querySelector('.ul');
      ul.addEventListener('click', function () {
        console.log('ul');
      })

      document.addEventListener('click', () => {
        console.log('document');
      })

      window.addEventListener('click', () => {
        console.log('window');
      })
    }
  </script>

Chrome 输出下顺序是:button -> li -> ul -> document -> window

如果是捕获的话,那么则相反

四、练习
点击一个 input 依次触发的事件

  <input type="text" id="text">

  <script>
    const text = document.getElementById('text');
    text.onclick = function (e) {
      console.log('onclick');
    }
    text.onfocus = function (e) {
      console.log('onfocus');
    }
    text.onmousedown = function (e) {
      console.log('onmousedown');
    }
    text.onmouseenter = function (e) {
      console.log('ommouseenter');
    }
    // text.onmouseup = function (e) {
    //   console.log('onmouseup');
    // }
  </script>

正确顺序是:onmouseenter -> onmousedown -> onfocus -> onclick。

如果加上 onmouseup,那就是:
onmouseenter -> onmousedown -> onfocus -> onmouseup -> onclick

五、阻止冒泡

event.stopPropagation(); 
 <script>
    btn.addEventListener('click', function () {
      console.log('button');
      event.stopPropagation();
    })
  </script> 

通过阻止冒泡,程序只会输出 button,而不会继续输出 li 等。

六、onmouseover 和 onmouseenter 区别
onmouseover 和 onmouseenter 区别
这两者都是移入的时候触发,但是 onmouseover 会触发多次,而 onmouseenter 只在进去的时候才触发。

七、科普
并不是所有的事件都有冒泡,例如:
onblur
onfocus
onmouseenter
onmouseleave

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值