DOM事件委托

206 阅读2分钟

代码

  • HTML
<div class=爷爷>
  <div class=爸爸>
    <div class=儿子>
 文字
   </div>
  </div>
 </div>

给这三个div分别添加事件监听,fnYe、fnBa、fnEr

  • 提问1 点击了谁

当我们点击 文字 时,算不算点击儿子?算不算点击爸爸?算不算点击爷爷?

答案:都算

  • 提问2 调用顺序

点击文字,最先调用哪个函数

答案:都行

IE5认为先调用fnEr,网景认为先调用fnYe

2002年,W3C发布标准

规定浏览器必须同时支持两种调用顺序:

浏览器首先按照 爷爷=>爸爸=>儿子 顺序查查看有没有函数监听

然后按照 儿子=>爸爸=>爷爷 的顺序看有没有函数监听

有函数监听就调用,并提供事件信息,没有就跳过

术语:

由内向外找监听函数,叫事件冒泡

由外向内找监听函数,叫事件捕获

示意图:

addEventlistener

事件绑定API

IE5*:baba.attachEvent('onclick',fn) //冒泡

网景:baba.addEventListener('click',fn) //捕获

W3C:baba.addEventListener('click',fn,bool)

如果 bool 传或为 false

就让fn走冒泡,浏览器在冒泡baba有fn监听函数,就会调用fn

如果 bool 为 true

就让fn走捕获。。。

代码示例

注意 e对象被传给所有监听函数,事件结束后,e对象就不存在了。e.target 是用户操作的元素

取消冒泡 && 不可取消冒泡

捕获不可取消,但冒泡可以

e.stopPropagation()可中断冒泡,浏览器不再向上走。一般用于封装某些独立的组件

不可取消冒泡

MDN搜索 scroll event,看到Bubbles和Cancelacle

意思是不可取消冒泡

自定义事件

浏览器事件

一共100多种事件,列表在MDN

用户可以自定义事件

示例

事件委托

例如给100个按钮添加点击事件

监听这100个按钮的祖先,等冒泡的事件判断target是不是这100按钮中的11个

优点

省监听数(内存)

可以监听动态元素

封装事件委托

用户传入四个参数(事件,元素,用户操作的元素,回调函数)

on('click','#div1','button',()=>{
    console.log('button 被点击了')
})

简洁版(有bug)

function on(eventType,element,selector,fn){
    if(!(element instanceof Element)){
        element = document.querySelector(element)
    }
    element.addEventListener(eventType,(e)=>{
        const t = e.target
        if(t,maches(selector)){
            fn(e)
        }
    })
}

完整版

function on(element, eventType, selector, fn) {
    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
  }