DOM 事件模型

334 阅读3分钟

事件是用户或者浏览器自己执行的某种动作,是文档或者浏览器发生的一些交互瞬间,比如点击(click)按钮等,这里的click就是事件的名称。JS 与 HTML 之间的交互是通过事件实现的,DOM支持大量的事件

事件分发和DOM事件流

众所周知,DOM 是个树形结构,我们在页面上点击一个元素,事件对象将通过 DOM 事件流确定的 DOM 树进行传播。

事件对象被调度到事件目标。但是在分发之前,必须首先确认事件对象的传播路径。

在事件传播过程,IE 认为应该先从最小的子元素开始查找事件对象,然后往上传给每一级的父元素,这种由内向外的查找事件对象方式,叫事件冒泡

而网景认为应该从最顶层的元素开始查找,逐层往下找,这种由外向内的查找事件对象方式,叫事件捕获

最后,2002年,W3C 发布标准,规定浏览器应该同时支持两种调用顺序,于是有了如图的事件流。

事件流分为三个阶段:事件捕获阶段、目标阶段、事件冒泡阶段。

目标阶段,活动对象到达事件对象的事件目标。如果事件类型指示事件不会冒泡,则该事件对象将在此阶段完成后停止。

开发者可以自由选择将事件对象放在捕获阶段还是冒泡阶段。

事件绑定

  • IE 5:element.attachEvent('onclick', fn)// 冒泡
  • 网景:element.addEventListener('onclick', fn)// 捕获
  • W3C:element.addEventListener('onclick', fn, bool)

如果 bool 不传或为 falsy ,fn 就会走冒泡阶段。即当浏览器进入冒泡阶段发现 element 有 fn 监听函数,就会调用 fn 函数,并提供事件信息。

如果 bool 为 true ,fn 就会走捕获阶段。即当浏览器进入捕获阶段发现 element 有 fn 监听函数,就会调用 fn 函数,并提供事件信息。

事件冒泡和事件捕获实例

注意:e 对象被传给所有监听函数。事件结束后,e 对象就不存在了。

target v.s currentTarget

区别

  • e.target 是用户操作的元素
  • e.currentTarget 是程序员监听的元素

特例

当只有一个 div 被监听,fn 分别在捕获阶段和冒泡阶段监听 click 事件

div.addEventListener('click', f1)
div.addEventListener('click', f2, true)

此时,谁先监听就会先执行。

取消冒泡和不可阻止默认动作

取消冒泡

捕获接断不可取消,但是冒泡阶段可以取消。我们通过 e.stopPropagation() 可以中断冒泡,浏览器不再向上走。一般用于封装某些独立的组件。

不可阻止默认动作

有些事件不能阻止默认动作。以 scroll event 为例,看到 Bubbles 和 Cancelable。

  • Bubbles 为该事件是否冒泡,所有冒泡都可取消
  • Cancelable 为开发者是否可以阻止默认事件

自定义事件

我们也可以自定义事件

element.addEventListener('click', () => {
    const event = new CustomEvent('custom', {
        "detail": {name: "custom", age: 18}
    })
    element.dispatchEvent(event)
})
element.addEventListener('custom', (e) => {
    console.log('custom')
    console.log(e)
})