DOM事件和事件委托

190 阅读4分钟

DOM事件

DOM事件模型和事件流

DOM事件模型分为捕获和冒泡。一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。

(1)捕获阶段:事件从window对象自上而下向目标节点传播的阶段;

(2)目标阶段:真正的目标节点正在处理事件的阶段;

(3)冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。

事件捕获和事件冒泡

  • 从外向内找监听函数,叫事件捕获(爷爷=>爸爸=>儿子)

  • 从内向外找监听函数,叫事件冒泡(儿子=>爸爸=>爷爷)

  • 示意图

图片1.png

事件绑定API

  • IE 5*:baba.attachEvent('onclick', fn) // 冒泡

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

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

如果 bool 不传或为 falsy

  • 就让 fn 走冒泡,即当浏览器在冒泡阶段发现 baba 有 fn 监听函数,就会调用 fn,并提供事件信息

如果 bool 为 true

  • 就让 fn 走捕获,即当浏览器在捕获阶段发现 baba 有 fn 监听函数,就会调用 fn ,并提供事件信息

  • 可以选择把fn放在哪边

图片2.png

DOM事件捕获和冒泡的具体流程

  • 捕获是从上到下,事件先从window对象,然后再到document(对象),然后是html标签(通过document.documentElement获取html标签),然后是body标签(通过document.body获取body标签),然后按照普通的html结构一层一层往下传,最后到达目标元素。

  • 而事件冒泡的流程刚好是事件捕获的逆过程。

代码

let n = 1

level1.addEventListener('click', (e)=>{
  const t = e.currentTarget
  setTimeout(()=>{
    t.classList.remove('x')
  }, n * 1000 )
  n += 1
},true)
level1.addEventListener('click', (e)=>{
  const t = e.currentTarget
  setTimeout(()=>{
    t.classList.add('x')
  }, n * 1000 )
  n += 1
})
...

图片3.png

W3C 事件模型

  • 先捕获(先爸爸=>儿子)再冒泡(再儿子=>爸爸)

  • 注意 e 对象被传给所有监听函数

  • 事件结束后,e 对象就不存在了

target v.s. currentTarget

区别

  • e.target // 用户操作的元素

  • e.currentTarget // 程序员监听的元素

  • this 是 e.currentTarget,不推荐使用它

  • event.target指向引起触发事件的元素,而event.currentTarget则是事件绑定的元素,只有被点击的那个目标元素的event.target才会等于event.currentTarget。也就是说,event.currentTarget始终是监听事件者,而event.target是事件的真正发出者。

举例

  • div > span{文字},用户点击文字

  • e.target 就是 span

  • e.currentTarget 就是 div

取消冒泡

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

  • e.stopPropagation() 可中断冒泡,浏览器不再向上走

  • e.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。

不可阻止默认动作

有些事件不能阻止默认动作

  • Bubbles 的意思是该事件是否冒泡,所有冒泡都可取消

  • Cancelable 的意思是开发者是否可以阻止默认事件

  • Cancelable 与冒泡无关

4.png

  • 所有冒泡皆可取消,默认动作有的可以取消有的不能取消

  • Cancelable 是用来取消(也可以说阻止)默认动作的

如何阻止滚动

scroll 事件不可阻止默认动作

  • 阻止 scroll 默认动作没用,因先有滚动才有滚动事件

  • 要阻止滚动,可阻止 wheel 和 touchstart 的默认动作

  • 注意:需要找准滚动条所在的元素

  • 滚动条还能用,可用 CSS 让滚动条 width: 0

event. preventDefault()

  • 如果调用这个方法,默认事件行为将不再触发。

6.png

x.addEventListener('wheel',(e)={
   e.preventDefault()
}) //阻止页面滚动
x.addEventListener('touchstart',(e)={
   e.preventDefault
}) //阻止手机页面滚动

CSS 也行

  • 使用 overflow: hidden 可以直接取消滚动条

  • 但此时 JS 依然可以修改 scrollTop

总结:

target 和 currentTarget

  • 一个是用户点击的,一个是开发者监听的

取消冒泡

  • e.stopPropagation()

事件的特性

  • Bubbles 表示是否冒泡

  • Cancelable 表示是否支持开发者取消冒泡

  • 如 scroll 不支持取消冒泡

如何禁用滚动

  • 取消特定元素的 wheel 和 touchstart 的默认动作

自定义事件

浏览器自带事件

  • 一共100多种事件,可查询MDN

  • 在自带事件之外,自定义一个事件

7.png

事件委托

优点

省监听数(内存)

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
  • 如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能;因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件的时候再去匹配判断目标元素;

可以监听动态元素

  • 事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的。

8.png

  • Element.matches API 的基本使用方法: Element.matches(selectorString),selectorString 既是 CSS 那样的选择器规则。

JS支持事件吗

  • 支持,也不支持。本节课讲的 DOM 事件不属于 JS 的功能,术语浏览器提供的 DOM 的功能

  • JS 只是调用了 DOM 提供的 addEventListener 而已