目录
- 什么是事件冒泡
- 什么是事件捕获
- 如何控制事件在冒泡阶段执行还是捕获阶段执行
- 什么是事件代理
- 事件代理和事件委托的区别
- 事件代理在实际应用中到底有什么用?
- 如何阻止事件冒泡
关于事件代理的一切
什么是事件冒泡
事件冒泡就是:当某元素执行某一种类型事件,那么从该元素起逐级向外层的元素检测是否存在与本身同样的事件。这一个过程,就叫做事件冒泡。
举个例子:
有这么一个结构
div1 嵌套着 div2
div2 嵌套着 div3
然后我们给这三个元素分别绑定一个 click 事件并输出他们自身的 class。
(div1 的 class 为"div1",div2 的 class 为"div2",div3 的 class 为"div3")
问题是:在事件冒泡的阶段点击 div3 的话,输出的结果是什么?
答案是:div3,div2,div1 都会被输出。
当 div3 的事件被触发后,因为事件冒泡的机制,浏览器会去 div3 的父元素(也就是 div2)身上寻找同类型事件,如果找到了。则触发。
注意:不论有没有在 div2 上找到同类型事件,冒泡会一直传递下去,一直到 Document 为止。
这就是事件冒泡
所以,在事件冒泡的前提下,三个嵌套 div 的输出顺序是:div1 > div2 > div3
什么是事件捕获
当我们了解了什么是事件冒泡后,事件捕获就变得简单起来了。
因为,事件捕获刚好就和事件冒泡反过来。
事件捕获就是:当元素被触发事件时候,从该元素的根节点开始逐级向里寻找同类型事件。这个过程,就被称为事件捕获
还是拿 div1,div2,div3 举个例子。
在事件捕获的状态下。点击 div3 并不会优先触发 div3 的事件。而会先触发 div1 的事件,再然后是 div2,最后才是 div3。因为事件捕获是从根节点开始向内查找的。外部的元素上的事件会优先被检测到并执行。
所以在事件捕获的状态下,三个 div 的输出顺序是:div1 > div2 > div3
总结下事件冒泡和事件捕获的区别,事件冒泡是从元素本身开始,逐级向上寻找同类型事件并执行,一直到 document 为止。事件捕获是从 document 开始,逐级向下寻找同类型事件,并执行,一直到触发该事件的元素为止。
如何控制事件在冒泡阶段执行还是捕获阶段执行
我们先来看看官方提供的 DOM 事件流向模型图。
图上说明了:↓
事件的流向有三个阶段:捕获阶段,目标阶段,冒泡阶段。
红色的部分代表事件捕获,蓝色部分代表目标阶段。绿色的线代表冒泡阶段。
由图可知,当一个元素身上的事件被触发后,会优先从根节点进行事件捕获(捕获阶段),然后寻找到自身为止(目标阶段),最后从自身往外层逐级冒泡(冒泡阶段)。
所以,一次事件当执行,是走以下流程的
先进行事件捕获 => 再到目标本身 => 最后再进行事件冒泡
有的小伙伴可能就会疑惑了:如果事件每次触发都需要把冒泡和捕获都走一遍,那岂不是每个事件都需要触发两次?
不会的。
这里需要注意:事件冒泡和事件捕获本身并不会主动触发事件。需要我们决定事件在冒泡阶段执行还是在捕获阶段执行。
我们介绍一下: addEventListener 函数。
addEventListener 函数用于事件绑定,他有三个参数 ↓
- eventType 事件类型("click 之类的")
- function 触发事件后所需要执行的函数
- bool 入参 true/false,决定事件在冒泡阶段执行还是捕获阶段执行。 true 表示事件在捕获阶段执行,false 表示事件在冒泡阶段执行。默认值是 false。
还是拿这三个 div 举例子
(当第三个入参为 false 时)
(当第三个入参为 true 时)
由图可知:
当 addEventListener 函数的第三个参数输入的是 false 的时候。点击 div3,事件会从 div3 开始执行,并逐级向上执行。(冒泡阶段执行)
当 addEventListener 函数的第三个参数输入的是 true 的时候。点击 div3,事件会从 div1 开始执行,并逐级向下执行。(捕获阶段执行)
小知识 ↓
事件冒泡和事件捕获好像存在一个就足够了,为什么会有这两套概念?答:因为事件冒泡是微软公司提出来了。而事件捕获是网景公司提出来了,删掉那个都不好,干脆就两个都留下来了。
需要注意 ↓
在我们没有做任何处理的情况下,我们所绑定的事件,默认都在冒泡阶段执行。
什么是事件代理
事件代理:事件代理就是利用事件冒泡或事件捕获的机制把一系列的内层元素事件绑定到外层元素。(这也就意味着子元素本身将不用注册自己的事件)
举个例子 ↓
看上图,不难看出,其实只有 div1(最外层的元素)被绑定了事件。但为什么可以执行输出 div2 和 div3 呢?
他的执行流程是 ↓
1.当我们点击 div2(或 div3)的时候,浏览器发现 div2/div3 本身没有事件
2.通过冒泡的方式检查到 div3,发现 div3 中有一个 click 事件,就执行 div3 的 click 事件
3.这里注意:e 表示我们当前点击的事件,所以即使事件在 div3 执行。e 指向的还是 div2(或 div3),所以可以输出 div2(或 div3)的内容。
辟谣
网上有些小伙伴会说,只有事件冒泡阶段才可以有事件代理。
并不是!!!
实际上,不论是事件冒泡阶段还是事件捕获阶段,都可以完成事件代理。
冒泡阶段的事件代理写法 ↓
const parDom = document.getElementById("某ID");
parDom.addEventListener(
"click",
(e) => {
console.log("冒泡阶段的事件代理");
},
false
);
捕获阶段的事件代理写法 ↓
const parDom = document.getElementById("某ID");
parDom.addEventListener(
"click",
(e) => {
console.log("捕获阶段的事件代理");
},
true
);
事件代理和事件委托的区别
没有区别
叫法不同而已。事件代理和事件委托是同一个东西。
所以小伙伴注意:如果以后面试官问你事件代理和事件委托有什么区别,八成是在给你下套。
强调一遍:事件代理和事件委托这两个没有区别!!仅仅只是叫法不同而已。
事件代理在实际应用中到底有什么用?
事件代理(事件委托)的适用场景其实我们大家接触到的还是蛮多的,只是大家可能都没发现还能这么玩而已。
事件代理的作用就是:避免大量事件注册
举个例子 ↓
咱们有一个列表,列表项需要绑定事件。
像这样,是不是就需要注册 9 个事件?上图只有 9 个,如果这个列表有 1 万行。难道我们就需要去注册 1 万个事件吗?这显然不合理。所以事件代理诞生了。把事件注册到父元素上,不论我们有多少个列表,都只需要在父元素上注册一个事件就好了。
我们对上图的列表注册做一个优化 ↓
这样,我们就将 N 个事件合并为一个事件了!
对性能更友好。
如何阻止事件冒泡/捕获
阻止事件冒泡/捕获的方法 : event.stopPropagation( )
例如:
function fun(event){
...
event.stopPropagation( )
}
尽量不要用 event.stopPropagation()去阻止捕获阶段执行的事件。因为捕获阶段是从外到内,如果我们要触发内部的某一个事件,那么 event.stopPropagation()会让浏览器检测在第一层就阻止往下捕获,也就检测不到我们写在内部的事件。所以 event.stopPropagation()在事件冒泡阶段执行的函数上用用就好了。
总结
什么是事件冒泡 ↓
事件冒泡就是:当某元素执行某一种类型事件,那么从该元素起逐级向上面的元素检测是否存在与本身同样的事件。这一个过程,就叫做事件冒泡。
什么是事件捕获 ↓
当元素被触发事件时候,从该元素的根节点开始逐级向下寻找同类型事件。这个过程,就被称为事件捕获
如何控制事件在冒泡阶段执行还是捕获阶段执行
addEventListener(“event”,fun,bool)的第三个入参,false 代表事件在冒泡阶段执行,true 代表事件在捕获阶段执行。默认都是 fasle。
什么是事件代理
事件代理就是利用事件冒泡或事件捕获的机制把一系列的内层元素事件绑定到外层元素。交给外层元素统一触发。
事件代理和事件委托的区别
没区别,叫法不同而已。
事件代理(事件委托)在实际应用中到底有什么用?
避免大量事件注册
如何阻止事件冒泡/捕获
在函数体中写 event.stopPropagation()即可阻止冒泡或捕获
附加 ↓
在我们没有做任何处理的情况下,我们所绑定的事件,默认都在冒泡阶段执行。
作者:工边页字 链接:https://juejin.cn/post/7118717854962155533 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。