基于js的事件捕获和冒泡原理
为了理解什么是事件捕获和冒泡,我们需要理解什么是事件。
什么是事件?
我们知道,在前端开发中,JavaScript负责定义网页的“行为”。这里的“定义”其实是指开发者可以通过JavaScript语言向浏览器描述一些规则,浏览器根据这些规则与用户进行交互。例如,开发人员希望当用户单击页面上的按钮时,会弹出一个显示特定内容的窗口。当用户实际点击此按钮时,浏览器将弹出指定窗口,并根据开发人员定义的规则显示指定内容。
在上面的例子中,浏览器是所有规则的执行者,开发人员是这些规则的制定者,而JavaScript只是开发人员用来向浏览器描述这些规则的语言(否则,浏览器无法知道开发人员在什么情况下想要做什么)。假设我们通过以下语句向浏览器描述一个规则:
正文按钮id=' BTN ' Click/button script varbutton=document . getelementbyid(' BTN ');//获取按钮按钮。addeventlistener ('click ',function(){//定义点击事件提醒('我被点击');})/脚本/正文
现在页面上有一个按钮,我们先用原生DOM得到这个按钮,然后用button的语法。addeventlistener ("click ",function () {})向浏览器描述一个规则:点击此按钮时,会弹出一个提示框,显示“我被点击了”。用户点击按钮后,网页上会出现如下提示:
浏览器称这种“点击”为“事件”。“事件”用于描述交互过程中的一些具体关键点(如点击、鼠标滑动、滚轮滚动、键盘按压、触摸屏操作等)。每个操作对应一个特定的事件,但该事件可能与用户行为无关,例如,网页加载后也是一个事件)。浏览器处理交互最重要的手段是执行开发人员基于事件定义的回调函数(如用户“点击按钮”时的“弹出窗口”,回调函数即addEventListener中的函数定义“弹出窗口”的行为)。
定义这个规则后,当用户点击按钮时,浏览器会弹出上面的窗口。我们调用在这个按钮上触发的“click”事件(因为我们的回调函数绑定到这个按钮)。
那么什么是事件的捕捉和冒泡呢?
事件捕获和冒泡
这个问题与HTML的结构密切相关。
在前端开发中,我们使用标签语言HTML来描述网页结构,如标题、段落、表格等。这些网页元素描述了需要在网页上显示的内容,它们构成了整个网页的“骨架”,通常是一个嵌套结构,例如:
Html标题.//这是网页内容/头体的元描述//这是网页需要渲染的真实内容。div h1 Title /h1 p这里是一个段落/p /div /body/html上面网页的结构图如下(没有设置填充等属性,子元素通常用父元素填充,这里的内部间距只是为了说明元素的嵌套关系):
我们可以看到,body元素是整个网页的容器,里面包含了一个div元素,div里面包含了h1和P两个元素,如果我们现在点击P的内部,是不是点击了它的外部容器div和最外面的body?
从浏览器的角度来看,我们同时点击了这三个元素。
要证明这个结论很简单,只需使用addEventListener将click事件分别绑定到div和body。如果点击p也会被触发,那么上述结论是正确的。毫无疑问,它们会被触发。
那么问题来了,既然用户同时点击这三个元素,浏览器应该执行第一个定义的回调函数是哪个元素(因为JavaScript采用单线程模型,回调函数的执行一定要有一定的顺序)?
这个问题实际上是说嵌套元素应该从内到外还是从外到内响应事件。浏览器之争的对立双方各有看法:网景认为最外层主体应该先得到这个事件,然后是div,最后是目标元素p;微软的IE开发团队认为应该是内部P先得到这个事件,然后div,最后body。在没有标准约束的情况下,他们根据自己的想法设计浏览器事件模型。网景从外到内传播的模式在业内称为事件捕捉模式,微软从内到外传播的模式称为事件冒泡模式。
虽然两种模型在思维上有所不同,但都可以保证所有绑定的回调函数都被正确触发(但触发顺序相反。如果这个触发序列很重要,那么在那个时候,你的代码可能只能在一个浏览器中正确运行,或者是恶心的浏览器兼容。但是,浏览器允许开发人员在事件传播过程中防止事件继续传播,因此它们之间的区别变得极其明显。
假设我们在为单击div元素定义回调函数时防止事件扩散:
div.addEventListener('click '),函数(e){ 0.e . stopperpagation();//防止事件扩散})这个代码会在两个模型之间产生巨大的差异。在捕获模型中,由于最外面的部分首先获得事件,因此首先触发body的click事件,然后是div的click事件。由于事件传播被阻止,p元素不会触发回调。相反,冒泡模型中,内部的P先得到事件,然后是div,所以回调会被P和div触发,身体因为没有冒泡而无法监听事件。相同的代码在两个模型中产生完全不同的行为,这显然是开发人员无法接受的(两个模型都有各自的适用场景和自身的合理性,因此模型的质量不能一概而论)。
那么后来的国际标准组织是如何解决这个冲突的呢?答案是由开发者来选择。
标准事件绑定使用addeventListener函数,该函数接收两个必需参数和一个可选参数:必需参数为Event(事件名称,如‘cick’)和function(回调函数),可选参数为useCapture(是否使用Capture模型,默认值为false。根据MDN的接口描述,也可以在这里传入一个对象来设置这个监控的其他参数。详见MDN接口文档-添加。
div.addEventListener('click ',function(){},true);//使用捕获模型的第三个参数是识别开发人员是否需要使用捕获模型。默认值为false,即默认使用微软的冒泡模型(这是因为大多数事件只在最里面的元素上触发,这间接表明冒泡模型具有更好的通用性)。如果开发人员的需求确实需要使用捕获模型,您可以将第三个参数设置为true。例如,以下示例:
事件捕获和冒泡的使用
在理解了事件捕获和冒泡的基本原理之后,我们举一个例子来说明这两个模型的基本用法。
假设以下DOM结构:
div id=' outer ' div id=' inner ' style=' width :100 px;高度: 100像素;' border: 1px纯黑;'/div /div这是两个重叠的div,当被点击时,两者都会响应这个点击事件。假设事件绑定如下:
var outer=document . query select or(' # outer ');var inner=document . query selector(' # inner ');外层。addeventlistener ('click '),函数(e) {alert('来自外部div的消息');e . stopperpagation();//防止事件向内部扩散},true);//使用内部捕获模型。addeventlistener ('click '),函数(e) {alert('来自内部div的消息');},真);//使用捕获模型的页面上只会显示外部弹出消息,内部事件被e.stopPropagation()拦截,因此不会触发事件。如果您编写以下代码:
var outer=document . query select or(' # outer ');var inner=document . query selector(' # inner ');外层。addeventlistener ('click '),函数(e) {alert('来自外部div的消息');},false);//使用冒泡模型内部。addeventlistener ('click '),函数(e) {alert('来自内部div的消息');e . stopperpagation();//防止事件向外扩散},false);//使用冒泡模型,这一次只显示内部消息,不显示外部消息,表示事件在向上冒泡过程中被阻止。
注意
如果表格中嵌入了复选框,则在单击行时无法选中复选框,也无法通过停止传播阻止复选框响应单击事件。测试发现checkbox状态改变的事件似乎不是由click事件触发的(断点跟踪显示CheckBox状态在click回调执行之前发生了改变,不清楚是什么事件改变了所选状态)。下面是一个可以处理行点击的示例:
表格边框=' 1 '单元格间距=' 0' tr类=' Tr ' t input类=' checkbox ' type=' checkbox '/t表格的第一行/td/Tr类=' Tr ' t input类=' checkbox ' type=' checkbox '/t表格的第二行/TD/Tr/tablescript var Tr=document . queryselectorall('。tr’);//获取tr。foreach(function(item){//为每个tr绑定click事件,并手动选中复选框项。addeventlistener ('click ',function (e) {var checkbox=item。queryselector('。复选框');checkbox.checked=!复选框。选中;})})var CB=document . queryselectorall('。复选框');cb.forEach(函数(项){ item.addEventListener('click '),函数(e){ this.checked=!this.checked});})/script在这里,stopPropagation并不是用来阻止事件传播的,而是为CheckBox定义了一个额外的click事件来解决状态不变的问题(断点跟踪后,点击CheckBox时,状态会发生三次变化,第一次触发原生事件导致其状态发生变化,第二次执行tr click事件,第三次为CheckBox定制click事件)。也就是说,点击tr时状态改变一次,点击CheckBox时状态改变三次,功能正常。
在大多数情况下,事件由最里面的元素处理,因此冒泡模型被广泛使用,并且它成为绑定事件时使用的默认模型。
摘要
事件捕获和冒泡这两种模型比较简单,所以只要理解了原理,就可以轻松掌握stopPropagation的使用,防止事件传播。
浏览器的标准事件模型将事件传播过程分为三个阶段:捕获阶段、目标阶段和冒泡阶段。捕获阶段是指事件从最外层传播到最内层之前的整个过程,对应捕获模型;处于目标阶段意味着事件只是传播到目标元素;起泡阶段是指从最里面的元素向外扩散的整个过程。因此,我们可以看到,标准的浏览器事件模型将捕获模型和冒泡模型有机地结合在一起,使得开发人员能够以最简单的方式灵活地使用这两种模型。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:基于js的事件捕获和冒泡原理是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。