这两天仔细看了一下MDN上关于事件流机制和相关方法的文档,发现有个很大的误区。过去我一直以为stopPropagation()就是用来阻止事件冒泡的,甚至很多博客和菜鸟教程上都是这样写的。但实际上文档中对stopPropagation()的解释是:

The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases

阻止当前事件在捕获和冒泡阶段的进一步传播,我的理解就是阻止当前事件的继续传播,不仅阻止冒泡,也阻止捕获和目标阶段,只在于你是在哪个阶段调用stopPropagation()的。

事件流过程

一个事件被触发,它的事件流包括三个阶段:捕获、目标、冒泡。捕获阶段的传播路径是:window对象→document→HTML→body→...→target的直接父元素,从最外层最不具体的元素层层传递到最里层具体的元素。目标阶段就是事件目标元素上发生。冒泡阶段与捕获阶段的传播路径正好相反:target→...→body→HTML→document→window,一般会将目标阶段看作冒泡阶段的一部分。

事件的传播路径在事件开始传播之前就已经确定,如果在传播过程中DOM结构发生变化,那么也会按照根据原来的结构而生成的传播路径进行传播。

添加事件监听:addEventListener(eventType, function, useCapture)

useCapture为true,表示该监听器监听捕获阶段的事件,useCapture为false,则监听冒泡阶段的事件。如果是在事件目标元素上添加的事件监听,那么不管useCapture设置为true还是false都能监听到,而且此时的事件阶段eventPhase为2。

简单来说就是为某个元素添加一个事件监听器,当有事件传播到该元素时,如果事件处于捕获阶段,eventPhase=1,那么设置了useCapture为true的监听器就会响应。如果事件传播到该元素时处于冒泡阶段,eventPhase=3,那么设置了useCapture为false的监听器响应。如果事件传播到该元素时处于目标阶段,eventPhase=2,那么不管是设置了true还是false的监听器都能响应。

下面是个例子:

<div class="first">first
<div class="second">
<label>click me
<input type="checkbox" class="target">
</label>
</div>
</div>
<script>
let div1=document.getElementsByClassName('first')[0],
div2=document.getElementsByClassName('second')[0],
target=document.getElementsByClassName('target')[0];
div1.addEventListener('click',(e)=>{
alert('first: '+e.eventPhase+'1');
},true);
div1.addEventListener('click',(e)=>{
alert('first: '+e.eventPhase+'2');
},true);
div1.addEventListener('click',(e)=>{
alert('first: '+e.eventPhase);
},false); div2.addEventListener('click',(e)=>{
alert('second: '+e.eventPhase);
},true);
div2.addEventListener('click',(e)=>{
alert('second: '+e.eventPhase); },false);
target.addEventListener('click',(e)=>{
alert('target: '+e.eventPhase);
},true);
target.addEventListener('click',(e)=>{
alert('target: '+e.eventPhase);
},false);
</script>

点击CheckBox后,依次弹出“first:11”、“first:12”、“second:1”、“target:2”、“target:2”、“second:3”、“first:3”。

stopPropagation()

那么stopPropagation()方法就比较好理解了,如果在某个阶段在事件监听函数里调用了事件的stopPropagation()方法,那么该事件后续的传播过程都会被阻止,不论是捕获还是处于目标或者冒泡。

比如你在捕获阶段监听到事件,并且调用了stopPropagation()方法,那么后续的在子节点上的捕获过程(如果还有的话,如果是在捕获过程的最后一个节点上调用,那么就是直接阻止将要进行的目标过程)、处于目标过程、冒泡过程都会被阻止。如果在处于目标阶段调用,那么后续的冒泡过程被阻止。如果在冒泡过程调用,后续的在其他节点上的冒泡传播被阻止。

这里要注意的是,stopPropagation()是以元素节点间的传播为单位,如果后续的传播过程中事件需要从一个元素节点流转到它的父节点或子节点,那么stopPropagation()将会阻止这样的过程。也就是说在一个元素节点内,它是不起作用的,如果在调用stopPropagation()的元素节点内还存在其他事件监听,并且与调用stopPropagation()的监听器的响应规则是一样的,那么这些其他的监听依然会按顺序响应。

例如在上面的例子中,first有两个捕获阶段的事件监听器,在第一个里调用stopPropagation(),那么第二个监听依然会响应,但是后续的捕获、目标和冒泡过程都被阻止了。因此最终只会弹出first的两个弹窗:“first:11”、“first:12”;

stopImmediatePropagation()

而stopImmediatePropagation()在调用后就直接阻止事件的后续传播过程了,包括同一个元素节点内的过程。如果在调用stopImmediatePropagation()的元素节点上还有其他事件监听,那么在调用stopImmediatePropagation()后,其他事件监听也不会响应。

还是以上面的例子,如果在first的第一个捕获监听器里调用stopImmediatePropagation(),那么最终只会有一个弹窗:“first:11”。

如果在target的捕获监听器里调用stopImmediatePropagation(),弹窗结果是:“first:11”、“first:12”、“second:1”、“target:2”。

preventDefault()

很多人把preventDefault()和阻止冒泡、捕获搞混,实际上preventDefault()和事件传播过程没有关系,它的作用是阻止事件的默认行为,例如a标签在click事件后的默认行为是链接到新的页面,checkbox的click事件默认行为是将修改的选中状态生效。preventDefault()的作用则是阻止这些默认行为,它不会影响事件的传播,而且无论在事件的哪个阶段调用preventDefault(),都能阻止默认行为。

【20190415】JavaScript-事件流与stopPropagation()、stopImmediatePropagation()的误区解析的更多相关文章

  1. JavaScript事件流原理解析

    一.为什么会有这一篇的文章 国庆前几天由于任务比较重,要赶在国庆前把一个进度的任务开发完成,所以也就有点赶,但是却遇到了一个比较奇怪的Bug,导致了任务比预计的延迟了几个小时,对此深表遗憾,所以利用国 ...

  2. 浅析JavaScript事件流——冒泡

    一.什么是事件冒泡流 我们知道事件流指的是从页面中接受事件的顺序. 为了形象理解事件冒泡,可以想象三军主将诸葛亮,在帐内运筹帷幄,眼观六路耳听八方,这时候前方的战事情况就需要靠传令兵来传达,当第一位传 ...

  3. 【原】javascript事件流

    摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原 ...

  4. 深入理解javascript事件流

    摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原 ...

  5. 深入了解javascript事件流

    摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原 ...

  6. javaScript事件流是什么?

    一.事件 事件是文档或者浏览器窗口中发生的,特定的交互瞬间. 事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字. 事件是javaScript和DOM之间交 ...

  7. JavaScript事件流

    什么是JS事件流 早期的IE事件传播方向为由上至下,即从document逐级向下传播到目标元素:而Netscape公司的Netscape Navigator则是朝相反的方向传播, 也就是从目标元素开始 ...

  8. javascript 事件流及应用

    当页面元素触发事件的时候,该元素的容器以及整个页面都会按照特定顺序发生该元素的触发 事件,事件传播的顺序叫做事件流 1.事件流的分类: A.冒泡型事件(所有浏览器都支持)   由明确的事件源到最不确定 ...

  9. javascript事件流机制

    (1)冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发. IE 5.5: div -> body -> document IE 6.0: div ...

随机推荐

  1. 吴恩达机器学习笔记54-开发与评价一个异常检测系统及其与监督学习的对比(Developing and Evaluating an Anomaly Detection System and the Comparison to Supervised Learning)

    一.开发与评价一个异常检测系统 异常检测算法是一个非监督学习算法,意味着我们无法根据结果变量

  2. [Swift]LeetCode825. 适龄的朋友 | Friends Of Appropriate Ages

    Some people will make friend requests. The list of their ages is given and ages[i] is the age of the ...

  3. 【netty】(2)---搭建一个简单服务器

    netty(2)---搭建一个简单服务器 说明:本篇博客是基于学习慕课网有关视频教学.效果:当用户访问:localhost:8088 后 服务器返回 "hello netty"; ...

  4. 【从零开始自制CPU之学习篇07】最简单的ALU—全加器

    ALU是算术逻辑单元,是CPU中重要的一部分,因为CPU本质上就是不断重复最简单的计算.而我们这一版CPU的ALU部分更为简单,是一个只能做加法的ALU. 理论部分 我们需要一个能帮我们进行数学计算的 ...

  5. 如何在启用JWT Token授权的.NET Core WebApi项目中下载文件

    背景 前几天,做项目的时候遇到一个文件下载的问题.当前系统是一个前后端分离的项目,前端是一个AngularJs项目, 后端是一个.NET Core WebApi项目.后端的Api项目使用了Jwt To ...

  6. redis 系列16 持久化 RDB

    一.概述 Redis是内存数据库,一旦服务器进程退出,服务器中的数据库内存数据状态也会消失.为了解决这个问题,Redis提供了RDB 持久化功能,这个功能可以将redis在内存中的数据库状态保存到磁盘 ...

  7. Solr04 - 在Jetty和Tomcat上部署Solr单机服务

    目录 1 准备安装环境 2 通过内部Jetty服务器启动 3 通过配置Tomcat服务器启动 3.1 删除不需要的应用 3.2 修改服务端口 3.3 部署solr.war 3.4 扩展: 虚拟目录发布 ...

  8. Python的魔法函数

    概要 如何定义一个类 类里通常包含什么 各个部分解释 类是怎么来的 type和object的关系 判断对象的类型 上下文管理器 类结构 #!/usr/bin/env python # -*- codi ...

  9. ARP欺骗攻击

    一.ARP攻击概述 ARP攻击主要是存在于局域网中,通过伪造IP地址和MAC地址实现ARP欺骗,能够在网络中产生大量的ARP通信量使网络阻塞,攻击者只要持续不断的发出伪造的ARP响应包就能更改目标主机 ...

  10. .netcore2.0+pgsql 脚手架

    mystaging介绍 这是一个 .netcore+pgsql 的脚手架,可以一键生成实体对象和业务层接口,让开发人员无需关注底层变动,专注编写业务代码,它可以让你使用 .netcore2.0的新特性 ...