DOM事件和事件委托

67 阅读2分钟

DOM 事件模型

DOM的事件模型分为两种:

  • 捕获: 从外向内找监听函数,叫做事件捕获
  • 冒泡:从内到外找监听函数,叫做事件冒泡

DOM的事件机制

  1. 捕获阶段:事件从window对象自上而下向目标节点传播
  2. 目标阶段:目标节点处理事件
  3. 冒泡阶段:事件从目标节点自下而上向window对象传播

DOM1.png

事件绑定API

IE5:  no2.attachEvent('onclick',fn) //默认冒泡
网景: no2.addEventListener('click',fn) //默认捕获
W3C:  no2.addEventListener('click',fn,bool) // bool不传或者为falsy,则走冒泡。为true,则走捕获

取消冒泡

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

e.stoPropagation() //取消冒泡

在 MDN 上面可以搜索scroll event,可以看到bubbles和Cancelable。

  • Bubbles 的意思是该事件是否支持冒泡,所有冒泡都可以取消
  • Cancelable 的意思是开发者是否可以阻止默认事件,与冒泡无关。

DOM2.png

事件委托

事件委托通俗来说就是监听祖先元素。
由于冒泡阶段会从子节点向上传播到父节点,因此可以监听父节点来处理子节点的事件。

优点:

  1. 由于只需要监听一个父节点不需要一个个监听子节点,可以节约监听数,减少内存消耗。
  2. 可以监听动态生成的元素
//简单封装事件委托
function on(eventType, element, selector, fn){
  if(!(element instanceof Element)){
    element = document.querySelector(element)
  }
  element.addEventListener(eventType, (e) => {
    const t = e.target
    // 如果元素被指定的选择器字符串选择,Element.matches()  方法返回true; 否则返回false。
    if(t.matches(selector)){ 
      fn(e)
    }
  })
}

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





以下是扩展内容:上述代码存在bug

// js
setTimeout(() => {
  const button = document.createElement('button')
  const span = document.createElement('span')
  span.textContent = 'click'
  button.aooendChild(span) 
  div1.appendChild(button)
})
// 此时被操作的元素为span,即 e.target为span
// 上述代码bug:此时,若使用上述代码封装事件委托,点击按钮将无反应
<!-- html结构 -->
<div id="div1">
  <button>
    <span>click</span>
  </button>
</div>
// 封装事件委托
function on(eventType, element, selector, fn) {
  if(!(element instanceof Element)){
    element = document.querySelector(element)
  }
  element.addEventListener(eventType, e => {
    let el = e.target
    while (!el.matches(selector)) {
      if (element === el) {
        // 往上找祖先元素,不可超过element
        el = null
        break
      }
      el = el.parentNode
    }
    el && fn.call(el, e, el)
  })
  return element
},