[TOC]
冒泡 bubbling
一、当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
| 【示例】有3层嵌套FORM > DIV > P,它们各自拥有一个处理程序```jsx
1、图示<br /><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 /> |
| --- |
二、使用 on<event> 属性或使用 HTML 特性(attribute)或使用两个参数的 addEventListener(event, handler) 添加的处理程序,对捕获一无所知,它们仅在第二阶段和第三阶段运行。<br />三、为了在捕获阶段捕获事件,我们需要将处理程序的 capture 选项设置为 true
jsx
elem.addEventListener(…, {capture: true})
// 或者,用 {capture: true} 的别名 “true”
elem.addEventListener(…, true)
1、capture 选项有两个可能的值:<br />(1)如果为 false(默认值),则在冒泡阶段设置处理程序。<br />(2)如果为 true,则在捕获阶段设置处理程序。<br />四、虽然形式上有 3 个阶段,但第 2 阶段(“目标阶段”:事件到达元素)没有被单独处理:捕获阶段和冒泡阶段的处理程序都在该阶段被触发。
| 【示例】
jsx
<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));
| | —- |