-
2021-11-14 16:17:58
事件委托
简介:事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。
利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。
事件先冒泡后捕获
事件捕获
当一个事件触发后,从Window对象触发,不断经过下级节点,直到目标节点。在事件到达目标节点之前的过程就是捕获阶段。所有经过的节点,都会触发对应的事件。
当为事件捕获(useCapture:true)时,先执行body的事件,再执行div的事件
事件冒泡
当事件到达目标节点后,会沿着捕获阶段的路线原路返回。同样,所有经过的节点,都会触发对应的事件。
当为事件冒泡(useCapture:false)时,先执行div的事件,再执行body的事件
默认为false先冒泡后捕获
根据w3c标准,应先捕获再冒泡。若要实现先冒泡后捕获,给一个元素绑定两个addEventListener,其中一个第三个参数设置为false(即冒泡),另一个第三个参数设置为true(即捕获),调整它们的代码顺序,将设置为false的监听事件放在设置为true的监听事件前面即可。
还有一种,在执行捕获时,设置setTimeOut(方法名,0),把它放到下一个宏任务
更多相关内容 -
通俗易懂地理清js事件流中的捕获阶段,目标阶段和冒泡阶段的具体运行顺序
2021-01-08 13:24:02下面我用dom2级事件函数来变换捕获和冒泡,用eventPhase来判断事件的是捕获,还是目标阶段,还是捕获, document.addEventListener(事件,function ,true or false); true代表捕获,false代表冒泡事件 目标对象.... -
JS中绑定事件顺序(事件冒泡与事件捕获区别)
2020-10-20 13:48:41本文主要介绍了JS中绑定事件顺序(事件冒泡与事件捕获区别)。具有很好的参考价值,下面跟着小编一起来看下吧 -
js冒泡、捕获事件及阻止冒泡方法详细总结
2020-12-01 02:07:10javascript, jquery的事件中都存在事件冒泡和事件捕获的问题,下面将两种问题及其解决方案做详细总结。 事件冒泡是一个从子节点向祖先节点冒泡的过程; 事件捕获刚好相反,是从祖先节点到子节点的过程。 给一个... -
JS中事件冒泡和事件捕获介绍
2020-11-27 17:36:18谈起JavaScript的 事件,事件冒泡、事件捕获、阻止默认事件这三个话题,无论是面试还是在平时的工作中,都很难避免。 事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。 事件冒泡阶段:... -
js之事件冒泡和事件捕获详细介绍
2021-01-19 19:02:40(1)冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。 IE 5.5: div -> body -> document IE 6.0: div -> body -> html -> document Mozilla 1.0: div -> body -> ... -
一个DOM元素绑定多个事件时,先执行冒泡还是捕获
2017-08-27 01:22:41绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他...问题引入:
一个DOM元素绑定两个事件,一个冒泡,一个捕获,则事件会执行多少次,执行顺序如何。
这次不卖关思了,直接给你个答案。不理解你就继续往下看。
绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。
addEventListener参数
element.addEventListener(type, function, useCapture) //【事件类型】,【事件处理程序】,【可选。布尔值,指定事件是否在捕获或冒泡阶段执行。true:事件句柄在捕获阶段执行;false:默认。事件句柄在冒泡阶段执行】
1.冒泡
冒泡是从下向上,DOM元素绑定的事件被触发时,此时该元素为目标元素,目标元素执行后,它的的祖元素绑定的事件会向上顺序执行。
如下代码所示,四个嵌套的div:
addEventListener函数的第三个参数设置为false说明不为捕获事件,即为冒泡事件。
<div id='one'> <div id='two'> <div id='three'> <div id='four'> </div> </div> </div> </div> <script type='text/javascript'> var one=document.getElementById('one'); var two=document.getElementById('two'); var three=document.getElementById('three'); var four=document.getElementById('four'); one.addEventListener('click',function(){ alert('one'); },false); two.addEventListener('click',function(){ alert('two'); },false); three.addEventListener('click',function(){ alert('three'); },false); four.addEventListener('click',function(){ alert('four'); },false); </script>
代码的执行顺序是:
点击one元素,输出one;
点击two元素,输出two one;
点击three元素,输出 three two one;
点击four元素,输出 four three two one;2.捕获
捕获则和冒泡相反,目标元素被触发后,会从目标元素的最顶层的祖先元素事件往下执行到目标元素为止。
将上面的代码第三个参数均改为true,事件为捕获阶段执行。
注意: 在事件处理程序中删除目标元素也能阻止事件冒泡,目标元素在文档中是事件冒泡的前提。则执行结果如下:
点击one,输出one;
点击two,输出one two;
点击three,输出one two three;
点击four,输出one two three four;很明显执行顺序是不同的。
3.当一个元素绑定两个事件,一个冒泡,一个捕获
首先,无论是冒泡事件还是捕获事件,元素都会先执行捕获阶段。
从上往下,如有捕获事件,则执行;一直向下到目标元素后,从目标元素开始向上执行冒泡元素,即第三个参数为true表示捕获阶段调用事件处理程序,如果是false则是冒泡阶段调用事件处理程序。(在向上执行过程中,已经执行过的捕获事件不再执行,只执行冒泡事件。)
如下代码:
one.addEventListener('click',function(){ alert('one'); },true); two.addEventListener('click',function(){ alert('two'); },false); three.addEventListener('click',function(){ alert('three'); },true); four.addEventListener('click',function(){ alert('four'); },false);
此时点击four元素,four元素为目标元素,one为根元素祖先,从one开始向下判断执行。
分析:
one为捕获事件,输出one;
two为冒泡事件,忽略;
three为捕获时间,输出three;
four为目标元素,开始向上冒泡执行,输出four;(从此处分为两部分理解较容易。)
three为捕获已执行,忽略;
two为冒泡事件,输出two;
one为捕获已执行,忽略。最终执行结果为: one three four two
例如,three作为目标元素,执行结果为:one three two(因为two是冒泡事件,在向下执行时没有执行到)。
执行次数:绑定了几个事件便执行几次。
如下代码,two元素绑定了两个不同事件,点击two都会执行这两个事件。而执行顺序有所差异。
one.addEventListener('click',function(){ alert('one'); },true); two.addEventListener('click',function(){ alert('two,bubble'); },false); two.addEventListener('click',function(){ alert('two,capture'); },true); three.addEventListener('click',function(){ alert('three,bubble'); },true); four.addEventListener('click',function(){ alert('four'); },true);
1、如果two为目标元素,目标元素的同类型事件按顺序执行,而其他元素根据W3C的标准执行,即先捕获后冒泡。
点击two执行结果:one(因为是two的父元素支持捕获事件所以先执行) two,bubble
two,capture(顺序执行,注意逗号不是间隔,是输出内容。)2、如果目标元素不是two,则two的同类型事件按先捕获后冒泡触发执行,也就是跟前面讨论的执行过程是一样的,只不过两个事件都绑定在同一个DOM元素上。
点击three执行结果:one two,capture three,bubble two,bubble
总结
所以,看到这里,你就应该明白了:
绑定在被点击元素的事件是按照代码顺序发生。
其他元素通过冒泡或者捕获“感知”的事件。
按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。举个例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> </style> </head> <body> <div class="outer"> <div class="inner"> <button id="btn">click</button> </div> </div> <script> const inner = document.querySelector('.inner'); const outer = document.querySelector('.outer'); const body = document.body; function h(stopPropagation){ return function(e){ console.log(`${this.id||this.className||this.tagName}`); if(stopPropagation){e.stopPropagation();} } } body.addEventListener('click',h()); //冒泡阶段执行 outer.addEventListener('click',h(),true); //捕获阶段执行 inner.addEventListener('click',h(true)); //冒泡阶段执行,取消冒泡 //解析:W3c执行顺序:其他元素的捕获事件,自身元素的顺序事件,其他元素的冒泡事件。 //此处, //body的click事件为冒泡阶段,暂不执行; //outer的click事件为捕获阶段执行,触发。输出outer //inner的click事件为冒泡阶段执行,本身触发,输出inner。 //但是因为inner在这里取消了冒泡,所以body的click冒泡事件也不能执行了。 </script> </body> </html>
结果是:
outer
inner<
改动,若此处:inner.addEventListener(‘click’,h()); //不取消冒泡
输出:
outer
inner
body遇到的坑
坑一:新插入的子元素没有绑定点击事件
一个Ul元素中初始的状态有4个li元素,我们可以循环给li元素添加click事件,执行我们想要的动作。这个例子的坑就在于,新添加的li元素不会有我们绑定的click事件。
<ul class="container"> <!-- 先循环给原有的4个li绑定click事件 --> <li class="item"></li> <li class="item"></li> <li class="item"></li> <li class="item"></li> <!-- 这是新添加的li元素,但是该元素没绑定click事件 --> <li class="item new"></li> </ul>
利用事件委托:
$('ul.container').click(function(event) { var target = event.target; if (target.className == 'item') { // dosomething } })
事件委托有很多好处:
- 新增的li元素也可以有同样的事件
- 给父元素(ul)绑定事件,那子元素(li)在出现的时候就立即有适当的事件了,不必等到后续js绑定上才能触发
- 不必循环多次访问dom,不必有很多事件处理程序,节省内存和绑定的时间坑二: 如果目标元素有子元素,那么怎么办?
<li class="item"> <div class="title">xxx</title> <p class="desc">xxxxxxs</p> </li>
解决办法:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>事件委托-li元素有多个子元素</title> </head> <body> <ul id="list"> <li>001</li> <li> <a>哈哈哈</a>li元素 </li> <li>003</li> <li>004</li> <li>005</li> </ul> <script> var $list = document.querySelector('#list'); $list.addEventListener('click',function(e){ var target = e.target||e.srcElement; //写法一 // while(target.nodeName.toLowerCase() !=='li'){ // target = target.parentNode; // } // console.log(target.innerHTML); //写法二: 将获取指定的li元素提取为一个函数 console.log(getNode(target).innerHTML); },false); function getNode(node){ if(node.nodeName.toLowerCase()==='li'){ console.log("node",node); return node; }else{ console.log('111'); return getNode(node.parentNode); } } </script> </body> </html>
坑三:谈JavaScript中的移除空事件处理程序
可以采用事件委托技术,限制建立的连接数量。另外,在不需要时,移除事件处理程序,也是解决这个问题的一种方案。
问题: 内存中留有那些过时不用的“空事件处理程序”,也是造成Web应用程序内存与性能问题的主要原因。
在两种情况下,可能会造成”空事件处理程序”:
第一种情况就是从文档 中移除带有事件处理程序的元素时。
- 这可能是通过纯粹的DOM操作,例如使用
removeChild()和replaceChild()
方法 - 但更多地是发生在使用innerHTML替换页面中某一部分的时候。
- 如果带有事件处理程序的元素被innerHTML删除了,那么原来添加到元素中的事件处理程序极有可能无法被当作垃圾回收。
常用场景:一个按钮被包围在div元素中,为避免双击,单击这个按钮就将这个按钮移除并替换成一条消息。
但问题在于,当按钮被从页面中移除时,它还带着一个事件处理程序呢,在
<div>
元素中设置innerHTML可以把按钮移走,但事件处理各种仍然与按钮保持着引用联系。有的浏览器(尤其是IE)在这种情况下不会作出恰当的处理,它们很有可能会将对元素和事件处理程序的引用都保存在内存中。如果你想知道某个元即将被移除,那么最好手工移除事件处理程序。
<div id="myDiv"> <input type="button" value="Click Me" id="myBtn"> </div> <script type="text/javascript"> var btn=document.getElementById("myBtn"); btn.onclick=function(){ btn.onclick=null; document.getElementById("myDiv").innerHTML="Processing…"; } </script>
注:采用事件委托也能解决这个问题。
如果事先知道将来有可能使用innerHTML替换掉页面中的某一部分,那么就可以不直接把事件处理程序添加到该部分的元素中,而通过把事件处理程序指定给最好层次的元素,同样能够处理该区域中的事件。
第二种导致“空事件处理程序”的情况,就是卸载页面中的时候。
毫不奇怪,IE在这种情况下依然是问题最多的浏览器,尽管其他浏览器或多或少也有类似的问题。
- 如果在页面被卸载之前没有清理干净事件处理程序。那它们就会滞留在内存中。每次加载完页面再卸载页面时(可能是在两个页面间来加切换,也可以是单击了“刷新”按钮),内存中滞留的对象数目就会增加,因为事件处理程序占用的内存并没有被释放。
一般来说,最好的做法是在页面卸载之前,先通过onunload事件处理程序移除所有事件处理程序。在此,事件委托技术再次表现出它的优势——需要跟踪的事件程序越少,移除它们就越容易。
对这种类似的操作,我们可把它想象成:只要是通过onload事件处理程序添加的东西,最后都要通过onunload事件处理程序将它们移除。
注:不要忘了,使用onunload事件处理程序意味着页面不会被缓存在bfcachek中,如果你在意这个问题,那么就只能在IE中通过onunload来移除事件处理程序了。
参考链接:
1、事件执行顺序介绍:http://www.cnblogs.com/greatluoluo/p/5882508.html
2、事件委托:http://www.cnblogs.com/greatluoluo/p/6282509.html - 这可能是通过纯粹的DOM操作,例如使用
-
JavaScript中捕获/阻止捕获、冒泡/阻止冒泡方法
2020-11-29 18:42:57提出事件流概念的正是IE和Netscape,但是前者提出的是我们常用的事件冒泡流,而后者提出的是事件捕获流。 第一部分:事件冒泡 即事件开始由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。 下面举... -
Js事件执行顺序,冒泡、捕获
2020-11-03 16:42:24Js事件执行顺序,冒泡、捕获 先绑定先执行,先捕获后冒泡1.Js事件执行满足“先绑定,先执行”的机制,如下:
HTML代码
<div class="demo1">事件</div>
JS代码
var demo1 = document.getElementsByClassName('demo1')[0]; demo1.addEventListener('click', function () { console.log('我先绑定') }); demo1.addEventListener('click', function () { console.log('我后绑定') });
结果:
交换JS绑定顺序var demo1 = document.getElementsByClassName('demo1')[0]; demo1.addEventListener('click', function () { console.log('我后绑定') }); demo1.addEventListener('click', function () { console.log('我先绑定') });
结果:
2.事件冒泡
事件冒泡是自下向上或由内而外进行的,即某元素接收执行事件后,会发生事件传递,逐次向上或向外传递给自己的父元素,直到window停止。
示例:
HTML代码,三层div嵌套<style> .demo1 { width: 300px; height: 300px; background-color: #f00; } .demo2 { width: 200px; height: 200px; background-color: #ff0; } .demo3 { width: 100px; height: 100px; background-color: #0f0; } </style> <div class="demo1"> <div class="demo2"> <div class="demo3"></div> </div> </div>
JS代码
demo1.addEventListener('click', function () { console.log('bubble demo1') }, false); // false表示冒泡 demo2.addEventListener('click', function () { console.log('bubble demo2') }, false); demo3.addEventListener('click', function () { console.log('bubble demo3') }, false); window.addEventListener('click',function(){ console.log('bubble window') }, false);
点击demo3时的执行结果(自下向上或由内而外,直到window):
3.事件捕获
事件捕获与事件冒泡正好相反,它是自上向下或由外而内进行的,即从window开始,向触发事件的元素传递事件。
示例:
HTML代码沿用上面事件冒泡的。
JS代码demo1.addEventListener('click', function () { console.log('capture demo1') }, true); // true表示捕获 demo2.addEventListener('click', function () { console.log('capture demo2') }, true); demo3.addEventListener('click', function () { console.log('capture demo3') }, true); window.addEventListener('click',function(){ console.log('capture window') }, true);
点击最内层demo3的执行结果(从window开始,自上到下或由外而内):
3.先捕获,后冒泡
事件捕获和冒泡的顺序是:先捕获后冒泡。
将上面示例的两个JS合到一起,点击内层demo3的执行结果(先捕获后冒泡):
-
事件冒泡和事件捕获的顺序
2019-08-01 12:42:22给同一个元素注册点击事件,测试事件冒泡和事件捕获的顺序,代码如下: <div class='container'> <div class='wapper'> <div class='context'> 点击你试试 </div> </div>...给同一个元素注册点击事件,测试事件冒泡和事件捕获的顺序,代码如下:
<div class='container'> <div class='wapper'> <div class='context'> 点击你试试 </div> </div> </div> <script> var container = document.querySelector('.container'); var wapper = document.querySelector('.wapper'); var context = document.querySelector('.context'); container.addEventListener('click',function(){ console.log('冒泡外'); },false); wapper.addEventListener('click',function(){ console.log('冒泡中'); },false); context.addEventListener('click',function(){ console.log('冒泡内'); },false); container.addEventListener('click',function(){ console.log('捕获外'); },true); wapper.addEventListener('click',function(){ console.log('捕获中'); },true); context.addEventListener('click',function(){ console.log('捕获内'); },true); </script>
运行结果是:
我们可以看到外层元素先触发事件捕获,后触发事件冒泡。内层元素先触发事件冒泡,再触发事件捕获。这个事件触发的顺序和绑定事件的先后顺序没有关系,即使先给内层元素注册点击事件,最后触发的顺序还是如上图结果所示。
-
DOM事件阶段以及事件捕获与事件冒泡先后执行顺序(图文详解)
2021-01-19 16:22:16俗话说的好,好记性不如个烂笔头,这么多技术文章如果不去吃透,技术点很快就容易忘掉,下面是小编平时浏览的技术文章,整理的笔记,分享给大家。 开发过程中我们都希望使用别人成熟的框架,因为站在巨人的肩膀上会... -
事件冒泡和事件捕获详解
2019-11-20 08:47:37事件冒泡 事件冒泡:DOM 元素触发的一些事件通过 DOM 层级结构传播。这种传播过程称为事件冒泡。事件首先由最内层的元素开始,然后传播到外部元素,直到它们到根元素。 翻译:党DOM元素发生一些事件(例如点击 click... -
事件冒泡和事件捕获的执行顺序
2020-05-19 14:46:32之前看到过很多文章,文章都统一的说事件捕获执行在前,冒泡执行在后,实际上这是不严谨的,今天刚好有时间我们来捋一捋,直接举例子 <div id="div1"> 我是div1 <div id="div2">我是div2 <div ... -
冒泡和捕获的执行顺序
2018-03-29 15:59:26因为我们先执行捕获过程,看看这个例子中有哪几个是捕获的,有div2和div4,那么捕获又是从大到小,所以,先弹出div2,再弹出div4,捕获结束以后就该是冒泡了,那么冒泡的顺序呢?从小到大,从子到父,所以就先弹出... -
事件冒泡和捕获的执行顺序
2016-12-07 11:05:07给一个dom同时绑定两个点击事件,一个用捕获,一个用冒泡,会执行几次事件,会先执行冒泡还是捕获? -
如何让事件先冒泡后捕获
2019-09-29 18:00:15但是如果要实现先冒泡后捕获的效果,对于同一事件,监听捕获和冒泡,分别对应响应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后再执行捕获事件。 转载于:... -
Javascript事件流(事件捕获、事件冒泡),事件委托(代理)
2021-01-08 10:50:17Javascript事件流(事件捕获、事件冒泡)–>事件委托(代理) Javascript与HTML之间的交互是通过“事件”实现的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间,当我们与浏览器中的web页面进行特定的交互时... -
JS之事件流(事件冒泡和事件捕获),阻止事件冒泡的方法,事件委托
2021-12-25 11:09:54后来在W3C组织的统一之下,JS支持了冒泡流和捕获流,但是目前低版本的IE浏览器还是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),所以为了能够兼容更多的浏览器,建议大家使用冒泡流。 事件流包含 -
冒泡与捕获
2021-03-14 19:54:17冒泡与捕获 事件流 冒泡型事件 例:div -> body -> html -> document IE5.5及以前版本,会跳过 html 直接 body -> document IE9、Firefox、Safari、Chrome将事件一直冒泡到 window 不是所有事件都能冒泡... -
javascript 中事件冒泡和事件捕获机制的详解
2021-01-19 16:46:33javascript 中事件冒泡和事件捕获机制的详解 二者作用:描述事件触发时序问题 事件捕获:从document到触发事件的那个节点,即自上而下的去触发事件—由外到内 事件冒泡:自下而上的去触发事件—由内到外 绑定事件... -
dom 同时绑定两个点击事件,一个用捕获,一个用冒泡,说下会执行几次事件,然后会先执行冒泡还是捕获?
2020-11-01 01:16:38当父元素与子元素都绑定了多个事件,且有的绑定在冒泡阶段、有的绑定在捕获阶段时,事件的触发顺序如何?如果你只关心这个问题,请直接下滑到3. 绑定多个事件,且由用户行为触发。如果你想细致了解JavaScript中的... -
Android-传统事件捕获和冒泡的流程解析
2019-08-13 05:53:28传统事件捕获和冒泡的流程解析 -
DOM事件流,冒泡 | 捕获 | 委托 ,详解
2021-01-08 08:03:49目录(一)DOM事件流① 定义② 图解③ 历史(二)冒泡(三)捕获(四)在不同的阶段执行事件(五)提高内存和性能 — 事件委托① 实例② 优点 (一)DOM事件流 ① 定义 DOM事件流包括三个阶段: 捕获阶段 目标阶段 ... -
DOM事件机制,事件捕获与事件冒泡先后执行顺序
2020-08-01 19:03:33DOM事件机制,事件捕获与事件冒泡先后执行顺序 DOM事件流的三个阶段 流的概念,在现今的JavaScript中随处可见。比如说React中的单向数据流,Node中的流,又或是今天本文所讲的DOM事件流,都是流的一种生动体现。用... -
一篇文章让你彻底弄懂JS的事件冒泡和事件捕获
2020-10-19 09:39:04主要介绍了一JS的事件冒泡和事件捕获,通过代码举例详细描述了两者之间的差别,需要的朋友可以参考下 -
事件模型:事件委托、代理?如何让事件先冒泡后捕获
2021-05-20 21:31:09如何让事件先冒泡后捕获 事件委托: 又叫事件代理,利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。 原理: 事件冒泡机制,从最深的节点开始,然后逐步向上传播事件。 作用: ①支持为...