小编明天在做我的项目的时候,在我的项目的正文中发现了对于事件委托的正文(PS:尽管上面代码写的不是事件委托的代码图片),小编作为一个干了前端三四年的小菜鸟,仍然不晓得事件委托到底是个啥,想想羞愧,特意在Vue3这个专栏两头插了这个。
既然谈到了事件委托,就不能不提冒泡,那什么是事件冒泡呢?小编特意在网上找了这样一张图片
上面的泡泡都很小,越往上泡泡越大。图中的泡泡能够看成是html中的DOM构造。那到底什么是冒泡呢?比方这样一个构造
<div onclick="handleDivClick()"> <ul onclick="handleUlClick()"> <li onclick="handleLiClick()">第一个元素</li> <li onclick="handleLiClick()">第二个元素</li> <li onclick="handleLiClick()">第三个元素</li> </ul></div>
同样,咱们再定义三个对应的函数
function handleDivClick(){ alert('handleDivClick')}function handleUlClick(){ alert('handleUlClick')}function handleLiClick(){ alert('handleLiClick')}
这个时候,咱们再点击li元素的时候,除了会执行自身的点击函数之外,还会执行ul和div元素的点击函数,也就是点击一次,会呈现三次弹窗。这个就是小编了解的冒泡,同样,在原生js中,提供了e.stopPropagation()(W3C)和e.cancelBubble = true(IE)形式阻止事件冒泡,对于Vue,同样提供了.stop修饰符。
以下内容转载自 https://www.cnblogs.com/lauzh...
那什么叫事件委托呢?它还有一个名字叫事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就能够治理某一类型的所有事件。那这是什么意思呢?网上的各位大牛们讲事件委托基本上都用了同一个例子,就是取快递来解释这个景象,我认真琢磨了一下,这个例子还真是失当,我就不去想别的例子来解释了,借花献佛,我摘过去,大家认真体会一下事件委托到底是一个什么原理:
有三个共事预计会在周一收到快递。为签收快递,有两种方法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。事实当中,咱们大都采纳委托的计划(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,而后依照收件人的要求签收,甚至代为付款。这种计划还有一个劣势,那就是即便公司里来了新员工(不论多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。
这里其实还有2层意思的:
第一,当初委托前台的共事是能够代为签收的,即程序中的现有的dom节点是有事件的;
第二,新员工也是能够被前台MM代为签收的,即程序中新增加的dom节点也是有事件的。
为什么要用事件委托:
一般来说,dom须要有事件处理程序,咱们都会间接给它设事件处理程序就好了,那如果是很多的dom须要增加事件处理呢?比方咱们有100个li,每个li都有雷同的click点击事件,可能咱们会用for循环的办法,来遍历所有的li,而后给它们增加事件,那这么做会存在什么影响呢?
在JavaScript中,增加到页面上的事件处理程序数量将间接关系到页面的整体运行性能,因为须要一直的与dom节点进行交互,拜访dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会缩短整个页面的交互就绪工夫,这就是为什么性能优化的次要思维之一就是缩小DOM操作的起因;如果要用事件委托,就会将所有的操作放到js程序外面,与dom的操作就只须要交互一次,这样就能大大的缩小与dom的交互次数,进步性能;
每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,天然性能就越差了(内存不够用,是硬伤,哈哈),比方下面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么咱们就能够只对它的父级(如果只有一个父级)这一个对象进行操作,这样咱们就须要一个内存空间就够了,是不是省了很多,天然性能就会更好。
事件委托的原理:
事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,而后逐渐向上流传事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比方给最外面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行程序a>li>ul>div,有这样一个机制,那么咱们给最里面的div加点击事件,那么外面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。
事件委托怎么实现:
终于到了本文的外围局部了,哈哈,在介绍事件委托的办法之前,咱们先来看一段个别办法的例子:
子节点实现雷同的性能:
<ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li></ul>
实现性能是点击li,弹出123:
window.onload = function(){ var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); for(var i=0;i<aLi.length;i++){ aLi[i].onclick = function(){ alert(123); } }}
下面的代码的意思很简略,置信很多人都是这么实现的,咱们看看有多少次的dom操作,首先要找到ul,而后遍历li,而后点击li的时候,又要找一次指标的li的地位,能力执行最初的操作,每次点击都要找一次li;
那么咱们用事件委托的形式做又会怎么样呢?
window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(){ alert(123); }}
这里用父级ul做事件处理,当li被点击时,因为冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的成果跟间接给节点的事件成果一样怎么办,比如说只有点击li才会触发,不怕,咱们有绝招:
Event对象提供了一个属性叫target,能够返回事件的指标节点,咱们成为事件源,也就是说,target就能够示意为以后的事件操作的dom,然而不是真正操作dom,当然,这个是有兼容性的,规范浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了以后节点的地位,并不知道是什么节点名称,这里咱们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,咱们须要转成小写再做比拟(习惯问题):
window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ alert(123); alert(target.innerHTML); } }}
这样改下就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!
下面的例子是说li操作的是同样的成果,要是每个li被点击的成果都不一样,那么用事件委托还有用吗?
<div id="box"> <input type="button" id="add" value="增加" /> <input type="button" id="remove" value="删除" /> <input type="button" id="move" value="挪动" /> <input type="button" id="select" value="抉择" /></div>window.onload = function(){ var Add = document.getElementById("add"); var Remove = document.getElementById("remove"); var Move = document.getElementById("move"); var Select = document.getElementById("select"); Add.onclick = function(){ alert('增加'); }; Remove.onclick = function(){ alert('删除'); }; Move.onclick = function(){ alert('挪动'); }; Select.onclick = function(){ alert('抉择'); }}
下面实现的成果我就不多说了,很简略,4个按钮,点击每一个做不同的操作,那么至多须要4次dom操作,如果用事件委托,能进行优化吗?
window.onload = function(){ var oBox = document.getElementById("box"); oBox.onclick = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLocaleLowerCase() == 'input'){ switch(target.id){ case 'add' : alert('增加'); break; case 'remove' : alert('删除'); break; case 'move' : alert('挪动'); break; case 'select' : alert('抉择'); break; } } }}
用事件委托就能够只用一次dom操作就能实现所有的成果,比下面的性能必定是要好一些的
当初讲的都是document加载实现的现有dom节点下的操作,那么如果是新增的节点,新增的节点会有事件吗?也就是说,一个新员工来了,他能收到快递吗?
看一下失常的增加节点的办法:
<input type="button" name="" id="btn" value="增加" /><ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li></ul>
当初是移入li,li变红,移出li,li变白,这么一个成果,而后点击按钮,能够向ul中增加一个li子节点
window.onload = function(){ var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; //鼠标移入变红,移出变白 for(var i=0; i<aLi.length;i++){ aLi[i].onmouseover = function(){ this.style.background = 'red'; }; aLi[i].onmouseout = function(){ this.style.background = '#fff'; } } //增加新节点 oBtn.onclick = function(){ num++; var oLi = document.createElement('li'); oLi.innerHTML = 111*num; oUl.appendChild(oLi); };}
这是个别的做法,然而你会发现,新增的li是没有事件的,阐明增加子节点的时候,事件没有一起增加进去,这不是咱们想要的后果,那怎么做呢?个别的解决方案会是这样,将for循环用一个函数包起来,命名为mHover,如下:
window.onload = function(){ var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; function mHover () { //鼠标移入变红,移出变白 for(var i=0; i<aLi.length;i++){ aLi[i].onmouseover = function(){ this.style.background = 'red'; }; aLi[i].onmouseout = function(){ this.style.background = '#fff'; } } } mHover (); //增加新节点 oBtn.onclick = function(){ num++; var oLi = document.createElement('li'); oLi.innerHTML = 111*num; oUl.appendChild(oLi); mHover (); };}
尽管性能实现了,看着还挺好,但实际上无疑是又减少了一个dom操作,在优化性能方面是不可取的,那么有事件委托的形式,能做到优化吗?
window.onload = function(){ var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; //事件委托,增加的子元素也有事件 oUl.onmouseover = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ target.style.background = "red"; } }; oUl.onmouseout = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ target.style.background = "#fff"; } }; //增加新节点 oBtn.onclick = function(){ num++; var oLi = document.createElement('li'); oLi.innerHTML = 111*num; oUl.appendChild(oLi); };}
看,下面是用事件委托的形式,新增加的子元素是带有事件成果的,咱们能够发现,当用事件委托的时候,基本就不须要去遍历元素的子节点,只须要给父级元素增加事件就好了,其余的都是在js外面的执行,这样能够大大的缩小dom操作,这才是事件委托的精华所在。
总结:
那什么样的事件能够用事件委托,什么样的事件不能够用呢?
适宜用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
值得注意的是,mouseover和mouseout尽管也有事件冒泡,然而解决它们的时候须要特地的留神,因为须要常常计算它们的地位,解决起来不太容易。
不适宜的就有很多了,举个例子,mousemove,每次都要计算它的地位,十分不好把控,在不如说focus,blur之类的,自身就没用冒泡的个性,天然就不能用事件委托了。
大家还能够扫描二维码,关注我的微信公众号,蜗牛全栈。