共计 6565 个字符,预计需要花费 17 分钟才能阅读完成。
小编明天在做我的项目的时候,在我的项目的正文中发现了对于事件委托的正文(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 之类的,自身就没用冒泡的个性,天然就不能用事件委托了。
大家还能够扫描二维码,关注我的微信公众号,蜗牛全栈。