JavaScript中的事件冒泡?事件传播的解释
注:本文来源 可译网
事件冒泡是你在学习javaScript旅途中遇到的一个术语,它涉及到当一个元素被另一个元素嵌套时调用事件处理的顺序,并且两个元素注册了同一个事件(例如,点击事件)。
但是事件冒泡仅仅是难题的一部分。它经常和事件捕获和事件传播一起被提及,并且对这三个概念有着很深的了解是学习javaScript事件必不可少的,例如,假如你想实现事件委托。
在这篇文章中,我会解释这些术语,并展示它们如何组合在一起.我还将向您展示如何对JavaScript事件流的基本了解可以让您对应用程序进行细粒度的控制。 请注意,这不是一个入门的事件,因此我们假定熟悉这个主题。 如果您想了解有关活动的更多信息,为什么不查看我们的书籍?: JavaScript: 忍者秘籍.
事件传播是什么?
让我们先从事件传播开始. 这是事件冒泡和事件捕获的总称。 考虑一个典型的标记来构建一个链接的图像列表,例如一个缩略图库:
- 1 <ul>
- 2 <li><a href="..."><img src="..." alt=""></a>
- 3 <li><a href="..."><img src="..." alt=""></a>
- 4 ...
- 5 <li><a href="..."><img src="..." alt=""></a>
- 6 </ul>
点击图像不仅为相应的IMG元素生成一个单击事件,但也为父元素,祖父元素等等生成点击事件, 一路通过所有元素的祖先元素,在window 对象之前终止。
在DOM术语中,图像是事件目标,点击来自最里面的元素。事件目标,加上它的祖先,从它的父元素到window 对象,在DOM树中形成一个分支。 例如,在图像库中,该分支将由节点组成:IMG,A,LI,UL,BODY,HTML,document,window。
请注意,窗口实际上并不是DOM节点,但它实现了EventTarget接口,因此,为了简单起见,我们正在处理它,就像它是文档对象的父节点一样。
这个分支是重要的,因为它是事件传播(或流动)的路径。这种传播是调用给定事件类型的所有侦听器的过程, 附加到分支上的节点。每个监听器将被调用一个事件对象,该对象收集与事件相关的信息(稍后再说)。
请记住,多个侦听器可以在同一事件类型的节点上注册。当传播到达一个这样的节点时,将按照注册的顺序调用侦听器。
还要注意那个分支确定是静态的,也就是说,它是在事件初始调度时建立的。在事件处理期间对树进行的改动将被忽略。
事件传播是双向的,从 window 到事件目标,然后再反转过来。这种传播可以分为三个阶段:
- 从 window 到事件目标的父级节点:这是 捕捉阶段
- 事件目标本身:这是 目标阶段
- 从事件目标的父级节点回到 window:冒泡阶段
通过调用监听器的类型可以区分这些阶段。
事件捕捉阶段
这个阶段只会调用 捕获者 监听器,就是说,那些监听器在注册的时候,addEventListener 的第三个参数使用了 true
值:
- 1 el.addEventListener('click', listener, true)
如果这个参数缺省,其默认值是 false
,事件监听器也就不是捕获者。
因此,在这一阶段,只有从 window 到事件目标路径上的捕获者会被调用。
事件目标阶段
这个阶段所有注册到事件目标的监听器都会被调用,其捕获标志的值不管是什么都没关系。
事件冒泡阶段
事件冒泡阶段只有不是捕获者的监听器会被调用。也就是说,在调用 addEventListener()
注册这些监听器的时候,第三个参数是false
。
- 1 el.addEventListener('click', listener, false) // listener doesn't capture
- 2 el.addEventListener('click', listener) // listener doesn't capture
注意,在捕获阶段中所有事件如 focus
、blur
、load
等向下流向事件目标时,不会冒泡。这个过程会在 目标 阶段后结束。
因此,到传播结束的时候,这个分支上所有监听器都确定会被调用一次。
事件冒泡不是在每一类事件上都会发生。在传播期间,监听器可以通过读取 event
对象的布尔属性 .bubbles
来获得某个事件是否会冒泡。
W3C UIEvents 规范中详细阐述了三个事件流阶段。
Copyright © 2016 万维网联盟 (MIT, ERCIM, Keio, Beihang).
访问传播信息
我提到过 event
对象的 .bubbles
属性。这个对象还提供了其它属性可以让监听器访问与传播相关的信息。
- e.target 引用事件目标。
- e.currentTarget 是监听器注册在其上的节点。它与调用监听器的上下文值相同,即
this
关键字引用的值。 - 我们甚至可以通过 e.eventPhase 知道当前是哪个阶段。这是一个整数,表示三个
Event
构造器常数CAPTURING_PHASE
、BUBBLING_PHASE
和AT_TARGET。
付诸实践
让我们把上述概念付诸实践。在下面的 pen〔译者注:指 codepen 的一项〕中,有 5 个嵌套的盒子,分别命名为 b0
…b4
。一开始,只有外层盒子 b0
可见;内层盒子会在鼠标滑过的时候显示出来。当我们点击某个盒子,在右边的表中会显示传播流的日志。
看看由 SitePoint (@SitePoint) 在 CodePen 上创建的 Pen jmXdpz。
甚至可以在框外点击:这种情况下,事件目标会是 BODY
或 HTML
元素,这取决于点击在屏幕上的位置
停止传播
事件传播可以由任何监听器通过调用事件对象的 stopPropagation 方法来停止。这就意味着传播路径上当前目标之后的所有节点上注册的监听器都不会被调用。相反,当前目标注册的监听器仍然会收到事件通知。
我们可以通过从之前的 Demo 派生出来的简单示例来检查这个行为,只需要在其中一个监听器中插入对stopPropagation()
的调用即可。现在我们已经在注册到 window 的监听器列表的最前面添加了一个新的捕获监听器:
- 1 window.addEventListener('click', e => { e.stopPropagation(); }, true);
- 2 window.addEventListener('click', listener('c1'), true);
- 3 window.addEventListener('click', listener('c2'), true);
- 4 window.addEventListener('click', listener('b1'));
- 5 window.addEventListener('click', listener('b2'));
- 6
这样,无论哪个盒子被点击,传播都会较早结束,只能到达 window
上注册的捕获监听器
立即停止传播
stopImmediatePropagation 正如其名称所表达的那样,会直接停止传播,阻止当前监听器的同级监听器接收事件。我们可以从对上一个 Pen 的小改动看到这一点:
- 1 window.addEventListener('click', e => { e.stopImmediatePropagation(); }, true);
- 2 window.addEventListener('click', listener('c1'), true);
- 3 window.addEventListener('click', listener('c2'), true);
- 4 window.addEventListener('click', listener('b1'));
- 5 window.addEventListener('click', listener('b2'));
- 6
现在不会有东西输出到日志表格,无论是来自 c1
捕获器还是 c2
捕获器,因为传播在执行新监听器之后就停止了。
事件取消
某些事件与浏览器在传播结束时执行的默认操作相关联。 比如, 单击链接元素或单击表单提交按钮会导致浏览器导航到新页面,或分别提交表单。
可以避免在事件取消时执行这样的默认行为,通过在侦听器中调用事件对象e.preventDefault 方式阻止默认行为。
结论
这样,我希望能够向你展示事件冒泡和事件捕获在JavaScript中是如何工作。如果您有任何问题或意见,我很高兴在下面的讨论中听到这些问题和意见。
参考
- Document Object Model (DOM) Level 2 Events Specification
- W3C DOM4 – Events
- DOM – Living Standard – Events
- W3C UI Events – DOM Event Architecture
- MSN – Events and the DOM
JavaScript中的事件冒泡?事件传播的解释的更多相关文章
- 探究JavaScript中的五种事件处理程序
探究JavaScript中的五种事件处理程序 我们知道JavaScript与HTML之间的交互是通过事件实现的.事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务 ...
- javascript必须懂之冒泡事件
在学习javascript中,如果在事件的使用上出现一些反差效果,不良效果,如鼠标的移入移出时,显示你所需要的内容, 但就是没有出现,然而你不断的检查代码,逐个代码查错,还在浏览器的调试工具中调试都没 ...
- 在Javascript中监听flash事件(转)
在Javascript中监听flash事件,其实有两种做法: 1.在特定的环境下(例如专门制作的flash),大家约定一个全局函数,然后在flash的事件中用ExternalInterface.cal ...
- JavaScript中的鼠标滚轮事件详解
JavaScript中的鼠标滚轮事件详解/*Firefox注册事件*/ ~~~Firefox: addEventListener('DOMMouseScroll', handler, false)if ...
- jquery中怎样防止冒泡事件
jquery中怎样防止冒泡事件 1.利用event.stopPropagation() 2.利用return false 3.利用event.preventDefault()
- 理解js事件冒泡事件委托事件捕获
js事件冒泡 javascript的事件传播过程中,当事件在一个元素上出发之后,事件会逐级传播给先辈元素,直到document为止,有的浏览器可能到window为止,这就是事件冒泡现象. <di ...
- 【事件流】浅谈事件冒泡&&事件捕获------【巷子】
首先在扯淡的时候我们需要先了解一个东西,这个东西就是事件流. 1.什么是事件流? 解释:当一个HTML元素触发一个事件处理函数的时候,该事件会在该元素节点到根节点之间传播,传播路径所经过的节点都会接受 ...
- 关于javascript中静态成员和实例成员的详细解释
关于javascript中静态成员和实例成员的详细解释 在我们了解什么是静态成员和实例成员之前,我们首先来了解一下什么是实例? 实例就是由构造函数创建出来的对象. 例如案例中 p 就是实例: fun ...
- JavaScript和JQuery中的事件\委托链\事件冒泡\事件捕获,兼容所有浏览器
有做过北大青鸟培训讲师经验的我,如今在一家公司做技术部经理的职位,发现有很多程序员的基本功相当糟糕,在组织企业内部培训时讲解了一些案例,总结了一些经典代码,希望对自己和有需要的人提供一些帮助吧: Ja ...
- day03—JavaScript中DOM的Event事件方法
转行学开发,代码100天——2018-03-19 1.Event 对象 Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态. 事件通常与函数结合使用, ...
随机推荐
- NO.10: 在operator=中处理 "自我赋值"
1.确保当对象自我赋值时operator=有良好的行为,其中的技术包括 "来源对象" 和 "目标对象" 的地址,精心周到的语句顺序,以及“ copy and s ...
- ping的作用
Ping是潜水艇人员的专用术语,表示回应的声纳脉冲,在网络中Ping 是一个十分好用的TCP/IP工具.它主要的功能是用来检测网络的连通情况和分析网络速度. Ping有好的善的一面也有恶的一面.先说一 ...
- P5002 专心OI - 找祖先
P5002 专心OI - 找祖先 给定一棵有根树(\(n \leq 10000\)),\(M \leq 50000\) 次询问, 求以 \(x\) 为 \(LCA\) 的点对个数 错误日志: 看下面 ...
- if语句和case语句用法展示
if语句和case语句用法展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.单分支if条件语句 1>.语法格式一 if [ 条件判断式 ];then 源代码 fi 2& ...
- Python基础【day02】:数据运算(二)
本节内容 数据运算 表达式while 循环 一.数据运算 算数运算: 比较运算: 赋值运算: 逻辑运算: 成员运算: 身份运算: 位运算: #!/usr/bin/python a = 60 # 60 ...
- Nginx的alias的用法及与root的区别
以前只知道Nginx的location块中的root用法,用起来总是感觉满足不了自己的一些想法.然后终于发现了alias这个东西. 先看toot的用法 location /request_path/i ...
- 【JUC】JDK1.8源码分析之CyclicBarrier
一.前言 有了前面分析的基础,现在,接着分析CyclicBarrier源码,CyclicBarrier类在进行多线程编程时使用很多,比如,你希望创建一组任务,它们并行执行工作,然后在进行下一个步骤之前 ...
- string和list互转
import string str = 'abcde' list = list(str) OR list = str.split() list ['a', 'b', 'c', 'd', 'e'] st ...
- JAVA迭代器学习--在JAVA中实现线性表的迭代器
1,迭代器是能够对数据结构如集合(ADT的实现)进行遍历的对象.在遍历过程中,可以查看.修改.添加以及删除元素,这是它与一般的采用循环来遍历集合中的元素不同的地方.因为,通常用循环进行的遍历操作一般是 ...
- Linux 环境变量问题
环境变量延伸: /etc/profile, /etc/bashrc, .bash_profile和.bashrc的差别 用户在登陆Linux操作系统的时候,"/etc/profile&quo ...