共计 1273 个字符,预计需要花费 4 分钟才能阅读完成。
序
最近遇到一个问题,当在一个 dom 元素中绑定了一个事件回调,然而触发该事件后事件的回调却未被执行,可能是什么起因呢?
通过一顿痴心妄想和对事件捕捉冒泡机制的一番温习之后,总结出了两个起因
- 事件在冒泡过程中,子元素阻止了该事件向上冒泡
- 事件在捕捉过程中,父元素阻止了该事件向下捕捉
切实想不出还有什么其余起因了,所以发这篇博客也是心愿有懂的大神给小弟指点迷津
事件流传机制
dom 事件的事件源(即在回调函数中通过 event.target
失去的 dom 元素),咱们把它称作事件的指标元素,在触发一个事件前后,指标元素只会经验一个阶段 — 指标阶段,而指标元素的所有父元素及其先人元素都会经验两个阶段 — 捕捉阶段和冒泡阶段。
如果当初有一个节点树是这样的:
document -> 先人 n -> ... -> 父亲 -> 指标元素
当触发指标元素的事件时,粗略地形容一下各个元素经验的过程就是:
document 捕捉 -> 先人 n 捕捉 -> ... -> 父亲捕捉 -> 指标触发事件 -> 父亲冒泡 -> ... -> 先人 n 冒泡 -> document 冒泡
即捕捉阶段是从上到下,而冒泡阶段是从下到上。如果咱们应用 element.addEventListener(callBack)
为一个元素增加事件回调时,该回调默认会在指标阶段(如果它是事件源)或冒泡阶段(如果它是事件源的先人)触发。
第一个起因曾经很清晰了,只有在子元素的回调函数中应用 stopPropagation 阻止该事件向上冒泡即可。
如果咱们进一步理解 addEventListener 这个办法,咱们就会发现除了回调函数以外,还能够传入一个 options
对象参数,来配置如何解决该事件。
const options = {
capture: Boolean, // 示意 `listener` 会在事件的捕捉阶段达到该元素时触发。once: Boolean, // 示意 `listener 在增加之后最多只调用一次 `
passive: Boolean // 示意 `listener` 永远不会调用 `preventDefault()`。}
咱们留神到这个对象有一个属性叫 capture
,如何将它设置为true
,示意回调函数会在该元素捕捉到事件时被调用,并且 stopPropagation 不仅能阻止事件向上冒泡,也能阻止事件向下捕捉,因而第二个起因也能够做到了,只有父元素设置事件捕捉阶段触发的回调,并且阻止事件的向下捕捉。(咱们常见的element.addEventListener(callBack, true)
也是一种设置回调在捕捉阶段执行的形式)
下面说到,事件源是没有捕捉阶段或是冒泡阶段的,所以在事件源的监听中设置 capture
是有效的,如果你想要事件源在监听到某个事件时阻止触发该元素上绑定的其余回调,那么你可能须要应用 stopImmediatePropagation。
如果想要加深一下事件捕捉和冒泡的机制,能够看看这个理论演示
点这里查看实例的源代码
最初
看到这里了,无妨再思考一个问题,如果在理论开发过程中发现某个元素的事件监听回调因为子元素的阻止冒泡而导致无奈触发,如何疾速定位出问题的子元素?