DOM事件流 && DOM0 - DOM3 事件模型 && DOM事件流应用场景(冒泡、事件委托)
DOM 元素是 JS 交互的基础,当点击 DOM 元素交互时,捕获事件的过程,并做出对应 JS 操作,因此理解事件流向是很重要的。
# 一、DOM 事件流
W3C 标准中规定了事件模型中,一次事件发生的三个过程:
- 事件捕获:DOM 树节点中,从外向内找监听函数;
- 事件目标: 找到对应 DOM 节点,有监听函数就调用,并提供对应事件信息(e.target) ,没有的话就跳过;
- 事件冒泡:从内向外查找监听函数
# 二、DOM 事件处理方式
除了 DOM 事件流的标准外,DOM 标准还包括 DOM 的事件模型:DOM0、DOM2、DOM3。它们分别规定了事件的处理阶段:捕获阶段还是冒泡阶段,还规定了怎么添加事件处理函数及个数等等。
# DOM0 事件
直接在标签中绑定 onclick 事件;或者在 js 中,获取 DOM 元素,再绑定 onclick = function(){}函数。
两者区别:标签直接绑定多个事件时,都会触发;而 js 绑定是,后面绑定事件会覆盖前面的。 共同点:事件监听都是在事件冒泡阶段。
# DOM2 事件
通过 addEventListener 和 removeEventListener 两个方法添加和移除事件。常用的有两个参数,实际上接收三个参数:
- 第一个是事件名(click)
- 第二个是事件处理函数
- 第三个 boolean 值,true 表示在事件在捕获阶段调用,false 为在冒泡阶段调用,默认值为 false。
特点:
- addEventListener 可以为元素添加多个事件处理函数,触发时会按照顺序依次调用;
- removeEventListener 不能移除匿名添加的函数。
# DOM3 事件
DOM3 事件是在 DOM2 事件基础上增加了事件类型:
- UI 事件,当用户与页面上的元素交互时触发,如:load、scroll
- 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
- 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
- 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
- 文本事件,当在文档中输入文本时触发,如:textInput
- 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
- 合成事件,当为 IME(输入法编辑器)输入字符时触发,如:compositionstart
- 变动事件,当底层 DOM 结构发生变化时触发,如:DOMsubtreeModified
# 三、DOM 事件流应用场景
# 事件冒泡
我们常说的事件冒泡,就是 DOM 事件流中的第三阶段。在冒泡阶段,如果监听到有对应函数,就会调用函数执行。常用的事件处理模型也默认的是执行冒泡阶段函数。
常见应用场景:整个红包券位信息,包含很多子节点信息,如果想要实现点击整个券位都可以跳转,同时实现点击信息上报,则可以将点击事件监听绑定在父节点上,通过 target(点击元素)和 currentTarget(监听元素)来区分实际点击券位。
当然,某些封装好的独立组件需要避免时间冒泡,可通过 e.stopPropagation()来取消冒泡(实际上取消的并不仅仅只是冒泡,它是阻止整个事件流的传递)。
提示
说到 e.stopPropagation(),还得和 e.preventDefault()区别区别。前者阻止的是事件传递,而后者阻止的是默认行为。元素 + 动作 = 默认行为,默认行为参考:https://web.dev/eventing-deepdive/#eventpreventdefault
# a trick - 这些代码让你的整个网页不可用
function preventEverything(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
document.addEventListener("click", preventEverything, true);
document.addEventListener("keydown", preventEverything, true);
document.addEventListener("mousedown", preventEverything, true);
document.addEventListener("contextmenu", preventEverything, true);
document.addEventListener("mousewheel", preventEverything, {
capture: true,
passive: false,
});
2
3
4
5
6
7
8
9
10
11
12
13
14
原理是把网页所有的事件都阻止传递,并且阻止了默认行为,表现:不能滚动、点击跳转、输入......
# 事件委托
目的:页面事件处理函数个数会影响页面整体性能,如果添加过多事件处理函数在子 DOM 节点上,会占用过多内存,影响页面性能,委托给父节点来处理就可以了。
原理:事件委托利用冒泡原理,把事件加到父元素或者更高以及元素身上,从而触发执行效果。
Codepen:实践出真知 (opens new window)
参考链接: