使用事件对象的stopPropagation/stopImmediatePropagation/preventDefault方法,可以中途取消事件处理。在本项中,将介绍如何正确使用这些方法。
●事件的传播
在介绍取消事件处理之前,再稍微看一点关于触发事件到调用事件处理为止的过程。目前为止,只介绍了「如果发生了事件就调用事件监听器」,但其实事件在到达特定的元素之前,还经历了如下的阶段。
①捕获阶段从window对象开始向下传播事件
②目标阶段找到触发事件的对象 发生事件
③冒泡阶段将底层元素中发生的事件向上传播
●事件的传播
首先,在捕获阶段,从最上层的window对象开始,沿着文档树向下传播事件。然后,在目标阶段找到触发事件的元素。
冒泡阶段,是从触发事件的元素开始朝着根元素传播事件的阶段。最终,在到达了最上层的window对象时结束事件的传播。事件传播到父元素的样子和气泡(bubble)上浮的样子很像,所以称为冒泡。
这里需要理解的是「事件监听器不仅仅只在触发事件的元素中执行」这一点。在捕获/冒泡阶段的过程中,如果有对应的事件监听器,这些也会依次执行。
我们来看下具体的例子吧。
●清单6-45 propagation.html(上)/propagation.js(下)
outer元素
inner元素
元素的click事件监听器
'触发了#inner监听器。'
'触发了#inner监听器2。'
元素的click事件监听器
'触发了#outer监听器。'
对有父子关系的<div>
/<a>
元素分别设定click事件监听器。这时,点击链接,按照下面的顺序执行处理。
- 显示对话框(触发了#inner监听器)
- 显示对话框(触发了#inner监听器2)
- 显示对话框(触发了#outer监听器)
- 按照链接跳转页面
这是因为将触发事件的元素作为基点,向上依次执行事件监听器。也可以说是「在冒泡阶段处理了事件」。如果对同一个元素设定了多个事件监听器,则按照书写的顺序执行。
这个顺序,也可以使用addEventListener方法的第3个参数来改变。试着将示例的粗体字部分改为true。这次,得到了如下的结果。
- 显示对话框(触发了#outer监听器)
- 显示对话框(触发了#inner监听器)
- 显示对话框(触发了#inner监听器2)
- 按照链接跳转页面
从上层节点开始朝着触发事件的元素依次执行事件监听器。在捕获阶段处理了事件。
■取消事件的传播
有时,我们需要取消这些事件的传播、或者是事件处理时浏览器本身的动作。例如,在之前的例子中,「只执行<a id="inner">
元素中的事件监听器,忽略上层的事件监听器」等。
这时,需要使用stopPropagation方法。
例如,下面的代码是在之前的清单6-45中,对<a id="inner">
元素添加stopPropagation方法。
元素的click时间监听器
'触发了#inner监听器。'
'触发了#inner监听器2。'
'触发了#outer监听器。'
运行示例,点击链接,可以得到如下的结果。
- 显示对话框(触发了#inner监听器)
- 显示对话框(触发了#inner监听器2)
- 按照链接跳转页面
这是因为取消了向父节点的冒泡。当然,在捕获阶段执行事件监听器时,在上层节点调用stopPropagation方法也可以同样取消事件的传播。
■立刻取消事件的传播
stopPropagation方法是取消向上/向下传播事件,相对的,如果要立刻取消传播(=在同一个元素中注册的监听器也不执行),需要使用stopImmediatePropagation方法。
将清单6-46中的①如下改写,并同样地执行。
●清单6-47 event_cancel.js
e.stopImmediatePropagation();
下面,是运行结果。
- 显示对话框(触发了#inner监听器)
- 按照链接跳转页面
我们可以看到,在<div id="inner">
元素中注册的第2个click事件监听器没有执行。
■取消事件的默认动作
事件的默认动作是指例如点击锚标签是「页面跳转」,在输入框中按下键是「显示输入的字符」等浏览器默认的动作。使用事件对象的preventDefault方法可以取消这些动作。
同样的,像下面这样改写清单6-46的①并执行。
●清单6-48 event_cancel.js
e.preventDefault();
下面,是运行结果。
- 显示对话框(触发了#inner监听器)
- 显示对话框(触发了#inner监听器2)
- 显示对话框(触发了#outer监听器)
我们可以看到所有的传播都结束了之后,没有跳转页面。
Note 也有不能取消的事件
也有使用preventDefault方法不能取消的事件。事件是否可以取消,同样可以使用事件对象的cancelable属性来判断。如果事件可以取消,cancelable属性返回true。
Note 在事件处理器中取消
在事件处理器中,将false作为返回值返回,可以取消事件默认的动作。例如,下面的例子是取消contextmenu事件,使右键菜单不显示。想要实现应用自己特有的右键菜单时,需要像这样使浏览器默认的菜单无效。
<div oncontextmenu="return false;">...</div>
那么,介绍了所有的取消类的方法了,我们将其使用表格罗列出来,好好整理比较一下。
方法 | 传播 | 其他的监听器 | 默认的动作 |
---|---|---|---|
stopPropagation | 停止 | - | — |
stopImmediatePropagation | 停止 | 停止 | - |
preventDefault | - | - | 停止 |
●取消事件 |
也就是说要取消之后的事件传播、默认的动作,调用stopImmediatePropagation/preventDefault方法就可以了。