摘要:等等,挺在这里,虽然不仅一篇文章阐述了事件委托是利用了冒泡机制,得益于冒泡机制,但是,怎么得益的,怎么利用的。事件委托和冒泡机制有关系吗接下来我想引出本文的重点事件委托和冒泡机制有关系吗我认为就算有关系,关系也不大。
面试官提出的问题
我们在面试前端的过程中,经常会听到面试官问这样的问题:
如果我有一个页面,里面1000个元素都要绑定click事件,请问你要怎么做
如果你回答逐个绑定那估计可以直接回家了,面试官希望的答案是你来高谈阔论事件委托,你应该能给出方法并写出解决方案。
接下来,考官一定要问,这么做的好处是什么,或者你为什么用事件委托。
我认为好处主要有两个:
事件只需要绑定一次,而不是绑定1000次,提高效率
动态添加进父级的元素自动拥有事件
根据http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ 更专业的说法如下:
Fewer functions to manage. 管理较少的函数
Takes up less memory. 更少的内存消耗
Fewer ties between your code and the DOM.降低代码和dom之间的关联
Don’t need to worry about removing event handlers when changing the DOM via修改dom的时候不用考虑删除事件。
这还没完,面试官通常会接下来问,那么,你绑定在父级上,页面怎么知道你点击的是哪一个?
我想这应该回答,利用了事件的冒泡机制。
等等,挺在这里,虽然不仅一篇文章阐述了事件委托是利用了冒泡机制,得益于冒泡机制,但是,怎么得益的,怎么利用的。
于是好奇的我引申出另外一个复杂的议题,事件的绑定和执行机制。
事件执行很多文章里介绍了这个机制,这篇文章很简明详尽:
http://www.quirksmode.org/js/events_order.html
概括一下,就是由于历史原因,浏览器对事件的处理有两种模式,一种是先执行外面父级元素的事件(捕获模式 capturing),一种先执行内部元素的事件(冒泡模式bubbling)。这个概念就类似图层,一种是外面的在前面,另一种是里面在前面。
现代浏览器里参照w3c规范,采用了这两种方式并行的方式,简单的来说就是先捕获,再冒泡。(为什么,为什么,规定为一种不好么)
我们都知道为某个元素注册事件,是通过addEventListener这个方法,那么,我直接注册的事件,他是属于捕获执行呢,还是冒泡执行呢?还是说我捕获阶段执行一次,冒泡阶段再执行一次。难道说每次都执行两次?
不对,同样一次绑定的方法是执行一次,因为它要么属于捕获阶段,要么属于冒泡阶段。这两个阶段就像通向公司的两条路,你去公司是一条路,回来是另外一条路,你的事件是路上的小卖铺,它要么在去的那条路上,要么在回来的那条路上。当然你也可以两条路上都开一家小卖铺,虽说没什么意义,但是这样事件触发的时候确实执行了两次,不过这也能证明事件执行的两个阶段。
事件执行顺序的实验先忘掉上面的理论,下面我们来做个试验,记住下面的html,我们打算为out同时绑定两个事件,看看执行顺序是怎么样的
Click
var a = document.querySelector("div#out") var b = document.querySelector("div#inner1") a.addEventListener("click",function(e){ alert(e.target+"事件A") }) a.addEventListener("click",function(e){ alert(e.target+"事件B") })
我们可以看到,为同一个元素先后绑定两个事件,执行的顺序是从上到下的,把事件B提到A前面就会先执行B。
接下来我们试一下通过addEventListener第三个参数指定事件绑定在哪条路上,false为冒泡阶段,true为捕获阶段。
a.addEventListener("click",function(e){ alert(e.target+"外部元素在冒泡阶段") },false) a.addEventListener("click",function(e){ alert(e.target+"外部元素在捕获阶段") },true)
这下我们可以看到,不论冒泡在前面还是后面,都是先执行捕获阶段的那行代码,佐证了先前说道的w3c规范下的先执行捕获再执行冒泡的行为。
不过在这里的实验中,我无意发现了一个有趣的现象,当你把html改成没有子元素,比如
Click
这时候就不遵循先捕获再冒泡的原则了,看起来像是判断节点没有子元素,就不需要使用捕获和冒泡的流程,只采用先来后到的顺序。这其中的原理还望高手指教。
总结: 当一个页面元素包含子元素节点的时候,他在处理在其身上的绑定事件的时候,采用先执行捕获阶段的事件,再执行冒泡阶段的事件。而事件处于哪个阶段,是由addevnetlistener的第三个参数决定的。
阻止冒泡我们都知道,阻止冒泡是采用类似 stopPropagation()的方法。但是请考虑这样一个问题:
a.addEventListener("click",function(e){ alert(e.target+"外部元素在冒泡阶段") },false) a.addEventListener("click",function(e){ alert(e.target+"外部元素在捕获阶段") },true) b.addEventListener("click",function(e){ e.stopPropagation() alert(e.target+"内部元素上的事件") })
这段代码里,我点击b,事件触发的顺序是怎样的?
答案是:外部元素捕获 ---> 内部元素事件
因为捕获是永远优先执行的,内部元素由于不存在子元素,所以只有一个阶段,无所谓先执行后执行,由于自身没有冒泡事件,所以stoppropagation() 掠过自身,寻找父级的冒泡阶段上的事件,一次查找,全部给阻止掉。
所以,想要点击内部的时候无视外部事件,一定不要把外部的事件放在捕获阶段,就是说第三个参数不要设为true。
我们再把代码搞更复杂一些:
click
var a = document.querySelector("div#out") var b = document.querySelector("div#inner1") var c = document.querySelector("div#inner2") a.addEventListener("click",function(e){ alert("a在冒泡阶段") },false) a.addEventListener("click",function(e){ alert("a在捕获阶段") },true) b.addEventListener("click",function(e){ alert("b在冒泡阶段") },false) b.addEventListener("click",function(e){ alert("b在捕获阶段") },true) c.addEventListener("click",function(e){ alert(e.target+"内部元素事件") })
执行一遍,可以加深了对事件这个模式的理解,顺序是这样的
a捕获 ---> b捕获 ---> 内部事件 ---> b冒泡 ---> a冒泡
这里我感兴趣的是阻止冒泡会怎么样,测下来是,如果把stoppropagation() 放在b,b本身的冒泡还是会执行,那么同理如果放在c,c本身如果有冒泡事件也会执行,
所以stoppropagation()所做的事情可以这么理解,阻止父级元素冒泡阶段的事件。
事件委托和冒泡机制有关系吗?接下来我想引出本文的重点:事件委托和冒泡机制有关系吗?
我认为就算有关系,关系也不大。
我们先来看一下一个常见的事件委托例子:
// Get the element, add a click listener... document.getElementById("parent-list").addEventListener("click",function(e) { // e.target is the clicked element! // If it was a list item if(e.target && e.target.nodeName == "LI") { // List item found! Output the ID! console.log("List item ",e.target.id.replace("post-")," was clicked!"); } });
简言之,绑定在父类上一个事件,然后通过回调函数的参数获得当前点击的是哪一个元素,相当于把事件绑定在子元素身上。
请问这跟上文长篇累牍的冒泡机制有什么联系?
假设不存在冒泡或者捕获,在父类上点击到了子类或者不论点到哪,这个事件都是要执行的,子类这个元素还会作为引用传到函数体里,我实在看不出这个冒泡有什么关系,如果要什么导致事件委托可以实现,应当是函数体内的引用才是。
那么,这个引用是什么情况呢,继续上面的实验:
var a = document.querySelector("div#out") var b = document.querySelector("div#inner1") var c = document.querySelector("div#inner2") a.addEventListener("click",function(e){ alert("a在冒泡阶段") console.log(e.target)//inner2 console.log(this)//out console.log(e.currentTarget)//out },false) a.addEventListener("click",function(e){ alert("a在捕获阶段") console.log(e.target)//inner2 },true) b.addEventListener("click",function(e){ alert("b在冒泡阶段") console.log(e.target)//inner2 },false) b.addEventListener("click",function(e){ alert("b在捕获阶段") console.log(e.target)//inner2 },true) c.addEventListener("click",function(e){ alert(e.target+"内部元素事件") console.log(e.target)//inner2 })
我们能看到,不论在哪一层里,e.target都是你当前点击的本身,这毫不奇怪,因为e本身是一个event对象,比如这里的MouseEvent,里面还带了是否同时按下alt键,鼠标位置等信息,可见这个对象本身可以说是和绑定主体无关了,和事件有关。所以,和冒泡还是没啥关系。以上代码里还展示了两种获取事件执行主体的方法,分别是e.currentTarge 和 this
所以我的观点是,虽然提到js的事件委托通常都会联系到冒泡,但是就算当初没有设计冒泡和捕获,事件委托还是事件委托,它依赖的是event对象传递到监听函数里面了,和其他无关。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/110066.html
摘要:事故起因因为这个如何阻止子元素继承父元素事件,然后我只答出了自己知道的部分,对于我不知道,这里我把这些东西收集起来自己学习了,也方便他人参考。事件委托是怎么工作的事件委托和冒泡机制有关系吗中使用与 事故起因: 因为这个jQuery如何阻止子元素继承父元素事件?,然后我只答出了自己知道的部分,对于我不知道,这里我把这些东西收集起来.自己学习了,也方便他人参考。 Javascript ...
摘要:二事件委托机制知道了事件的捕获冒泡机制,我们可以利用它来实现更方便的程序控制,事件委托便是最典型的应用之一。下面来说说中的事件委托机制。 一、事件的捕获与冒泡 由W3C规定的DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监听函数。以下面的HTML结构为例: ...
摘要:可选,布尔值,指定事件是否在捕获或冒泡阶段执行,默认冒泡。适用范围参数介绍必须,字符串,事件名称。必须,指定事件触发时执行的函数。事件冒泡事件冒泡与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,直到根节点。 什么是事件 javascript与HTML之间交互就是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。事件在浏览器中是以对象的形式存在的,即event,...
阅读 3708·2023-04-26 00:56
阅读 2686·2021-09-30 10:01
阅读 961·2021-09-22 15:30
阅读 3915·2021-09-07 10:21
阅读 1506·2021-09-02 15:40
阅读 2750·2021-08-30 09:47
阅读 1234·2021-08-16 10:57
阅读 1862·2019-08-30 14:01