当前位置: 首页 > Web前端 > HTML

JS中的事件序列(事件捕获和冒泡)

时间:2023-04-02 23:06:19 HTML

Question如果一个元素和它的祖先元素注册了相同类型的事件函数(比如点击等),事件发生时事件函数的调用顺序是怎样的?例如,考虑以下嵌套元素:---------------------------------|外||------------------------|||内部|||--------------------------|||--------------------------------两个元素都有onclick处理函数。如果用户点击内部,将调用内部和外部事件处理程序。但是谁先来呢?在这两种模式的糟糕日子里,Netscape和M$对此有不同的看法。Netscape认为outer上的处理程序应该首先执行。这称为事件捕获。M$认为inner上的处理函数优先执行。这称为事件冒泡。两种观点在使用事件捕获时是针锋相对的事件捕获(eventcapturing)|------||------------------|外层||||----------||-----------|||内部\/|||--------------------------||EventCAPTURING|-------------------------------------外部触发的事件处理程序首先触发,然后是内层事件冒泡(eventbubbling)/\----------------||------------------|外层||||------------||------------|||内部|||||------------------------||EventBUBBLING|--------------------------------与事件捕获相反,当使用事件冒泡时,内部的事件处理程序首先被触发,然后是外部的W3C模型。W3C标准做出了妥协。W3C事件模型中发生的任何事件,首先(从它的祖先元素窗口)开始一路向下捕获,直到到达目标元素,然后再从目标元素开始冒泡。1.先从上到下抓取|||/\-------------------||--||------------------|外层||||||--------------||--||------------|||内\/|||||||||||2.到达目标元素后自下而上冒泡|||------------------------------||W3C事件模型|------------------------------------------而你,作为开发人员可以决定事件处理程序是在捕获阶段还是在冒泡阶段注册。如果addEventListener的最后一个参数为true,那么处理函数会在捕获阶段被触发;否则(false),会在冒泡阶段触发。例如下面的代码:varselector=document.querySelector.bind(document);选择器('div.outer').addEventListener('点击',(e)=>{selector('p:first-of-type').textContent+='外部点击!'},true)selector('div.inner').addEventListener('click',(e)=>{selector('p:first-of-type').textContent+='innerclicked!'},false)document.addEventListener('click',(e)=>{selector('p:first-of-type').textContent+='documentclicked!'},true)当点击内部元素时,下面的东西发生:点击事件在捕获阶段开始。在这个阶段,浏览器会在所有祖先元素的内部找到点击事件处理函数(从window开始)。结果找到了两个,分别在document和outer上,这两个事件处理函数的useCapture选项为true,说明在capture阶段注册了。因此,document外部和外部的点击处理功能被执行。继续向下搜索,直到到达内部元素本身。捕获阶段结束。此时进入冒泡阶段,执行inner上的事件处理器。事件命中目标元素后,开始向上冒泡Bubble,一路寻找冒泡阶段的祖先元素上是否注册了事件处理器。由于未找到,因此不会发生任何事情。最后的结果是:如果我们在冒泡阶段注册祖先元素的事件处理器(addEventListener的useCapture选项为false):varselector=document.querySelector.bind(document);selector('div.outer').addEventListener('click',(e)=>{selector('p:first-of-type').textContent+='外部点击!'console.log(e);},false)selector('div.inner').addEventListener('click',(e)=>{selector('p:first-of-type').textContent+='innerclicked!'console.log(e);},false)document.addEventListener('click',(e)=>{selector('p:first-of-type').textContent+='documentclicked!'},false)结果是:传统模型element.onclick=function(){}会在冒泡阶段注册。事件冒泡的应用例如:点击时的默认函数如果在document上注册一个点击函数:document.addEventlistener('click',(e)=>{},false)那么任何元素上的点击事件最终都会冒泡到这个事件处理函数并触发函数——除非前面的事件处理函数阻止了冒泡(e.stopPropogation(),这种情况下事件不会继续向上冒泡)注意:e.stopPropagation()只能阻止向上冒泡在冒泡阶段传播事件。如果被点击元素的祖先元素在捕获阶段注册了事件处理器:ancestorElem.addEventListner('click',(e)=>{//dosomething...},true)那么事件处理器上祖先元素仍将在捕获阶段被触发。因此,可以在文档上设置这样一个处理函数,当页面上的任意一个元素被点击时,都会触发这个处理函数。一个实际的例子是下拉菜单:当点击除下拉菜单本身以外的文档时,下拉菜单会被隐藏。在冒泡或捕获阶段,e.currentTarget指向当前事件处理程序附加到的元素。你也可以在事件处理程序中用this替换它。M$模型的问题在于M$模型中不支持e.currentTarget,更糟糕的是,这也不指向当前HTML元素。