美文网首页饥人谷技术博客
DOM事件与事件委托

DOM事件与事件委托

作者: 茜Akane | 来源:发表于2021-04-16 15:56 被阅读0次

    Get Started

    • DOM事件
      ○ 标准
      ○ 术语
      ○ addEventListener
      ○ 取消冒泡
      ○ 自定义事件
    • 事件委托

    DOM事件

    点击事件,先从这里开始研究

    代码

    <div class="1">
        <div class="2">
            <div class="3">
                文字 
            </div>
        </div>
    </div>
    

    给三个div分别添加了事件监听,当点击文字时,最先点击了哪个div,最新按调用哪个事件监听函数?

    2002年,W3C发布标准

    • 文档名伪DOM Level 2 Events Specification
    • 规定浏览器应该同时支持两种调用顺序
    首先按照最外层=>最里层(1->2->3)顺序看看有没有函数监听
    然后按照最里层=>最外层(3->2->1)顺序看看有没有函数监听
    • 有监听函数就调用,并提供事件信息,没有就跳过
    术语
    从外向内找监听函数,叫事件捕获
    从内向外找监听函数,叫事件冒泡
    注意:开发者可以自己选择把监听函数放在捕获阶段还是冒泡阶段。

    示意图

    image.png

    addEventListener

    • 事件绑定API

        x.attachEvent('onclick', fn)  // 冒泡(IE 5)
        x.addEventListener('click', fn)  // 捕获(网景)
        x.addEventListener('onclick', fn, bool)
    
    • 如果bool不传或为falsy
      就让fn走冒泡,即当浏览器在冒泡阶段发现x有fn函数监听,就会调用fn,并提供事件信息
    • 如果bool为true
      就让fn走捕获,即当浏览器在捕获阶段发现x有fn函数监听,就会调用fn,并提供事件信息
    • 决定走哪种方式,另一边就会按照顺序空跑。
    • 示例代码:http://js.jirengu.com/pupuz/6/edit?html,css,js,output
    • 示例代码笔记:事件信息e对象被传给所有监听函数,事件结束后,e对象就不存在了。若是延迟函数或者其他时候需要用到e对象时,需要copy一份到函数局部变量里使用。

    target V.S. currentTarget

    • 区别
      e.target - 用户操作的元素
      e.currentTarget - 程序员监听的元素
      this是e.currentTarget,但为了不搞混最好不要用。
    • 举例
      div > span{文字} ,用户点击文字
      e.target 就是span
      e.currentTarget 就是div

    一个特例

    • 背景
      只有一个div被监听(不考虑父子同时被监听)
      fn分别在捕获阶段和冒泡阶段监听click事件
      用户点击的元素就是开发者监听的
    • 代码
    div.addEventListener('click', f1)
    div.addEventListener('click', f2,true)
    

    答案:谁先监听谁先执行

    取消冒泡

    • 捕获不可以取消,但是冒泡可以
      e.stopPropagation()可以中断冒泡,浏览器不再向上走。一般用于封装某些独立的组件
      不可阻止默认动作
    • 有些事件不能阻止默认动作
      MDN搜索scroll event,看到Bubbles和Cancelable
      Bubbles的意思是该事件是否冒泡,所有冒泡都可取消
      Cancelable的意思是开发者是否可以阻止默认事件
      Cancelable与冒泡无关


      image.png

    插曲:如何阻止滚动

    • scroll事件不可阻止默认动作
      阻止scroll默认动作没用,因现有滚动才有滚动事件
      要阻止滚动,可阻止wheel和touchstart的默认动作
      注意需要找准滚动条所在元素,示例
      到那时滚动条还能用,可用CSS让滚动条width: 0
      ::-webkit-scrollbar{width: 0 !important}
    • CSS也行
      使用overflow: hidden可以直接取消滚动条
      但此时JS依然可以修改scrollTop
    • 代码
        //  pc端 
        div.addEventListener('wheel',(e) => {    // 滚轮事件
            e.preventDefault();  //  阻止默认事件
        })
        // 然后CSS设置 ::webkit-scrollbar{
            // width:0;!important
        //}   即可实现无滚动
        
        //  移动端
        div.addEventListener('touchstart',(e) => {   // 触摸事件
            e.preventDefault();  //  阻止默认事件
        })
    

    自定义事件

    • 浏览器自带事件
      一共100多种事件,MDN
    • 可以自定一个事件
    • 代码
      button1.addEventListener('click', ()=>{
        const event = new CustomEvent('frank',{
          detail:{name: 'frank', age:18},
          bubbles: true, //默认不进行冒泡,所以要加上
          cancelable: false
        })
        button1.dispatchEvent(event)
      })
      
      div1.addEventListener('frank', (e)=>{
        console.log('事件触发了')
        console.log(e.detail)
      })
      

    事件委托

    • 场景一
      要给100个按钮添加点击事件,需要监听这100个按钮的上层,等冒泡的时候判断target是不是这100个按钮中的一个
    • 代码


      image.png
    • 场景二
      监听目前不存在的元素的点击事件,也是监听上层,等点击的时候看看是不是想要监听的元素即可
    • 代码


      image.png
    • 优点
      省监听数(内存)
      可以监听动态元素

    封装事件委托

    • 要求
      写出函数on('click', '#div1', 'button', fn),当用户点击里面的button元素时,调用fn函数
    • 代码一
      判断target是否匹配'#div1'
      // 封装事件函数
      function on(eventType, element, selector, fn){
        if(!(element instanceof Element)){ // '#div'是个选择器
          element = document.querySelector(element)
        }
        element.addEventListener(eventType,(e)=>{
          const t = e.target
          if(t.matches(selector)){
            fn(e)
          }
        })
      }
      
    • 代码二
      递归判断,向上层找,一直到第二个参数锁给定的范围
      function on(eventType, element, selector, fn) {
        if (!(element instanceof Element)) { // '#div'是个选择器
          element = document.querySelector(element)
        }
        element.addEventListener(eventType, (e) => {
          let el = e.target
          while (!el.matches(selector)) {
            if (element === el) {
              el = null
              break
            }
            el = el.parentNode
          }
          el && fn.call(el, e, el)
        })
        return element
      }
      

    JS支持事件吗?

    • 不支持。这是DOM事件,不属于JS功能,属于浏览器提供的DOM功能
      JS只是调用了DOM提供的addEventListener而已
    • 如何JS支持事件?手写一个事件系统

    相关文章

      网友评论

        本文标题:DOM事件与事件委托

        本文链接:https://www.haomeiwen.com/subject/pgfelltx.html