DOM事件和事件委托

186 阅读2分钟

DOM 事件

使用 JS 操作 DOM,绕不开的就是事件这个概念。

2002年,W3C 发布标准,文档名为:DOM Level 2 Events Specification

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

  1. 首先按 爷爷 => 爸爸 => 儿子 的顺序看有没有函数监听
  2. 然后按 儿子 => 爸爸 => 爷爷 的顺序看有没有函数监听

其中:

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

注意:这并不代表事件会触发两次,开发者自己选择把事件放在捕获阶段还是放在冒泡阶段

/**
 * type: 表示监听事件类型的字符串
 * listener: 监听事件触发时的回调函数
 * options: false 表示 listener 走冒泡; true 表示 listener 走捕获
 */
addEventListener(type, listener, options)

// 示例
dom.addEventListener('click', () => {
  console.log('触发了点击事件')
})

target 和 currentTarget

  • e.target: 用户操作的元素
  • e.currentTarget: 程序员监听的元素
  • this 是 e.currentTaget (不推荐使用)

取消冒泡事件

捕获事件不可取消,但冒泡事件可以: e.stopPropagation()

注意:有些时间不可取消冒泡

MDN 搜索 scroll event,看到 Bubbles 和 Cancelable

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

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

如何阻止页面的滚动

  • 取消滚动元素的 wheel 和 touchstart 的默认动作
  • 将滚动元素的滚动条用 css 隐藏
// 阻止PC端
dom.addEventListener('wheel', e => {
  e.preventDefault()
})
// 阻止移动端
dom.addEventListener('touchstart', e => {
  e.preventDefault()
})
.dom::-webkit-scrollbar {
  width: 0 !important;
}

事件委托

场景1:给 100 个按钮添加点击事件

解决思路:监听这 100 个按钮的祖先,登报跑的时候判断 e.target 是不是这 100 个按钮中的一个

场景2:监听不存在的元素的点击事件

解决思路:监听上级元素,等点击的时候看是不是想要监听的元素即可

特点:减少事件数量,预言未来元素,避免内存外泄

/**
 * 封装事件委托
 * @param {String} eventType 事件名称
 * @param {String} element 要监听的元素的选择器
 * @param {String} selector 未来元素的选择器
 * @param {Function} fn 事件触发时的回调函数
 */
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) {
        el = null
        break
      }
      el = el.parentNode
    }
    el && fn.call(el, e, el)
  })
  return element
}