[TOC]

冒泡 bubbling

一、当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。

| 【示例】有3层嵌套FORM > DIV > P,它们各自拥有一个处理程序```jsx

FORM
DIV

P

1、图示<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/355497/1616485932331-7533ee15-5d85-40eb-99fb-89ecef197054.png#crop=0&crop=0&crop=1&crop=1&height=107&id=fymrw&margin=%5Bobject%20Object%5D&name=image.png&originHeight=107&originWidth=775&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4314&status=done&style=none&title=&width=775)<br />2、点击内部的<p>会首先运行onclick<br />(1)在该 <p> 上的。<br />(2)然后是外部 <div> 上的。<br />(3)然后是外部 <form> 上的。<br />(4)以此类推,直到最后的 document 对象。<br />3、结果<br />点击<p>,会看到3个alert:p -> div -> form | | --- | 二、几乎所有事件都会冒泡<br />1、focus事件不会冒泡。这是例外,而不是规定。 <a name="2qNwW"></a> ## event.target 一、父元素上的处理程序始终可以获取事件实际发生位置的详细信息。<br />二、引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过event.target访问。<br />三、event.target与this(=event.currentTaget)的区别<br />1、event.target:是引发事件的“目标”元素,它在冒泡过程中不会发生变化。<br />2、this:是“当前”元素,其中有一个当前正在运行的处理程序。 | 【示例】如果我们有一个处理程序 form.onclick,那么它可以“捕获”表单内的所有点击。无论点击发生在哪里,它都会冒泡到 <form> 并运行处理程序。<br />1、在form.onclick处理程序中<br />(1)this(=event.currentTarget)是 <form> 元素,因为处理程序在它上面运行。<br />(2)event.target 是表单中实际被点击的元素。 | | --- | <a name="w6YQy"></a> ## 停止冒泡 一、冒泡事件从目标元素开始向上冒泡。通常,它会一直上升到<html>,然后再到document对象,有些事件甚至会到达window,它们会调用路径上所有的应用程序。<br />二、任意处理程序都可以决定事件已经被完全处理,并停止冒泡<br />三、用于停止冒泡的方法是event.stopPropagation() | 【示例】如果你点击 <button>,这里的 body.onclick 不会工作:jsx | | --- | 四、不要在没有需要的情况下停止冒泡<br />1、有时event.stopPropagation()会产生隐藏的陷阱,可能以后会称为问题。 | 【示例】我们创建了一个嵌套菜单,每个子菜单各自处理对自己的元素的点击事件,并调用stopPropagation,以便不会触发外部菜单。<br />之后,我们决定捕获在整个窗口上的点击,以追踪用户的行为(用户点击的位置)。有些分析系统会这样做。通常,代码会使用 document.addEventListener('click'…) 来捕获所有的点击。<br />我们的分析不适用于被 stopPropagation 所阻止点击的区域。太伤心了,我们有一个“死区”。 | | --- | 2、没有真正的必要去阻止冒泡。一项看似需要阻止冒泡的任务,可以通过其他方式解决。解决方法如下:<br />(1)自定义事件<br />(2)将我们的数据写入一个处理程序中的event对象,并在另一个处理程序中读取该数据,就可以向父处理程序传递有关下层处理程序的信息。 | 【示例】下列哪个操作是W3C标准定义的阻止事件向父容器传递: <br />A. e.preventDefault()<br />B. e.cancelBubble=true<br />C. e.stopPropagation()<br />D. e.stopImmediatePropagation()<br />1、答案:C<br />2、解析:<br />DOM中的事件对象:(符合W3C标准) <br /> preventDefault() 取消事件默认行为 <br /> stopImmediatePropagation() 取消事件冒泡,同时阻止当前节点上的事件处理程序被调用。 <br /> stopPropagation() 取消事件冒泡,对当前节点无影响。 <br />IE中的事件对象: <br /> cancelBubble = true 取消事件冒泡 <br /> returnValue() 取消事件默认行为 | | --- | <a name="oRwAG"></a> ### event.stopImmediatePropagation() 一、如果一个元素在一个事件上有多个处理程序,即使其中一个停止冒泡,其他处理程序仍会执行。<br />1、event.stopPropagation()停止向上移动,但是当前元素上的其他处理程序都会继续运行。<br />2、event.stopImmediatePropagation(),可以用于停止冒泡,并阻止当前元素上的处理程序运行。使用该方法之后,其他处理程序就不会被执行。 <a name="UBheW"></a> # 捕获 capturing 一、DOM事件标准描述了事件传播的3个阶段<br />1、捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。<br />2、目标阶段(Target phase)—— 事件到达目标元素。<br />3、冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。 | 【示例】在表格中点击<td>的图片。点击 <td>,事件首先通过祖先链向下到达元素(捕获阶段),然后到达目标(目标阶段),最后上升(冒泡阶段),在途中调用处理程序。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/355497/1616489234674-01468d06-e267-4f1f-aeab-70eaad9f1364.png#crop=0&crop=0&crop=1&crop=1&height=378&id=N8aML&margin=%5Bobject%20Object%5D&name=image.png&originHeight=608&originWidth=665&originalType=binary&ratio=1&rotation=0&showTitle=false&size=68268&status=done&style=none&title=&width=413) | | --- | 二、使用 on<event> 属性或使用 HTML 特性(attribute)或使用两个参数的 addEventListener(event, handler) 添加的处理程序,对捕获一无所知,它们仅在第二阶段和第三阶段运行。<br />三、为了在捕获阶段捕获事件,我们需要将处理程序的 capture 选项设置为 truejsx elem.addEventListener(…, {capture: true}) // 或者,用 {capture: true} 的别名 “true” elem.addEventListener(…, true) 1、capture 选项有两个可能的值:<br />(1)如果为 false(默认值),则在冒泡阶段设置处理程序。<br />(2)如果为 true,则在捕获阶段设置处理程序。<br />四、虽然形式上有 3 个阶段,但第 2 阶段(“目标阶段”:事件到达元素)没有被单独处理:捕获阶段和冒泡阶段的处理程序都在该阶段被触发。 | 【示例】jsx
FORM
DIV

P

![image.png](https://cdn.nlark.com/yuque/0/2021/png/355497/1616489498322-e05e6339-0a5a-48d1-bc1f-2b9b5c6f6de1.png#crop=0&crop=0&crop=1&crop=1&height=110&id=uiy3S&margin=%5Bobject%20Object%5D&name=image.png&originHeight=110&originWidth=772&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4489&status=done&style=none&title=&width=772)<br /><br />1、如果你点击了 <p>,那么顺序是:<br />(1)HTML → BODY → FORM → DIV(捕获阶段第一个监听器):<br />(2)P(目标阶段,触发两次,因为我们设置了两个监听器:捕获和冒泡)<br />(3)DIV → FORM → BODY → HTML(冒泡阶段,第二个监听器)。 |
| --- |

五、event.eventPhase属性,告诉我们捕获事件的阶段数。但它很少被使用,因为我们通常是从处理程序中了解到它。<br />六、要移除处理程序,removeEventListener 需要同一阶段<br />1、如果我们 addEventListener(..., true),那么我们应该在 removeEventListener(..., true) 中提到同一阶段,以正确删除处理程序。<br />七、同一元素的同一阶段的监听器按其设置顺序运行<br />如果我们在同一阶段有多个事件处理程序,并通过 addEventListener 分配给了相同的元素,则它们的运行顺序与创建顺序相同

| 【示例】```jsx
elem.addEventListener("click", e => alert(1)); // 会先被触发
elem.addEventListener("click", e => alert(2));

| | —- |