持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
DOM事件传播有三个阶段
- 捕获阶段(capturing phase) 事件从window向下传播
- 目标阶段(target phase)事件到达目标元素
- 冒泡阶段(bubbling phase)事件从目标元素开始冒泡
捕获
事件处理程序的另一个阶段是捕获(capturing)。他很少被用在实际开发中,但有时是有用的。
通过DOM on<evnet>
属性和HTML on<evnet>
或使用两个参数addEventListener(event, handler)添加处理程序,对捕获一无所知,他们在目标阶段和冒泡阶段执行。
如果在捕获阶段捕获事件,需要将处理程序capture选项设置为true:
elem.addEventListener(..., {capture: true}) // 或者,用 {capture: true} 的别名 "true"
elem.addEventListener(..., true)
移除处理程序时,removeEventListener需要同一个阶段。如果addEventListener(...,true),那么我们应该在removeEventListener(...,true)中设置相同的阶段。
冒泡
当一个事件发生在一个元素上,他会首先运行在该元素的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
假设我们有 3 层嵌套 FORM > DIV > P
,它们各自拥有一个处理程序:
<form onclick="alert('form')">
FORM
<div onclick="alert('div')">DIV<p onclick="alert('p')">P</p> </div>
</form>
<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`));
}
</script>
为每个元素在捕获阶段和冒泡阶段添加点击事件,当点击p是执行顺序如下:
- 捕获阶段:HTML->BODY->FORM->DIV
- 目标阶段:触发两次,一次是捕获,一次是冒泡
- 冒泡阶段:DIV->FORM->BODY->HTML
event.eventPhase告诉我们事件所处的阶段。当前阶段(capturing=1,target=2,bubbling=3)。
停止冒泡
冒泡事件默认从目标元素向上冒泡,通常,会一直上升到html,然后再到document对象,有些事件甚至会到达window,他们会调用路径上所有的处理程序。
- event.stopPropagation()会停止向上冒泡,但是当前元素上的处理程序会继续执行。
- event.stopImmadiatePropagation()方法,可以用于停止冒泡,并且阻止当前元素上的处理程序运行。 不建议停止冒泡,因为我们不确定是否确实不需要冒泡上来的事件。