一、什么是事件委托
原理:利用冒泡的原理,把事件加到父级上,触发执行效果。
二、为什么使用事件委托
一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;
每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。
三、怎么实现事件委托
直接上例子:
一个ul标签里面有多个li,点击每个li的时候,弹出“li”
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var li=document.getElementsByTagName('li');
for(var i=0;i<li.length;i++){
li[i].onclick=function(){
alert('li');
}
}
没错,我们应该是这样写的呀,试试也证明这样确实每点击一个li弹出“li”,
但是,首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li;浪费时间内存,
那么我们看看用事件委托如何实现:
var ul=document.getElementsByTagName('ul')[0];
ul.onclick=function(){
alert('li');
}
这里用父元素ul来做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的。
那么,如果我只想让点击li时才出发事件,点击ul时不触发,怎么办,,么办,办。。
别怕,每个事件都有事件源,我们可以通过事件源的获取来判断是ul呢还是li,那么事件源怎么获取:
事件对象(Event对象)提供了一个属性叫target,捕获真正被点击的节点元素,
注意:
不管在哪个事件中,只要你操作的那个元素就是事件源。
ie下:window.event.srcElement
标准下:event.target
nodeName:找到事件源元素的标签名
这样就简单了,,我们可以通过给父元素添加事件监听器。当有事件触发监听器时,然后检查事件的来源,排除非li子元素事件。如果是一个li元素,我们就找到了目标!如果不是一个li元素,事件将被忽略:代码如下:
var ul=document.getElementsByTagName('ul')[0];
ul.addEventListener('click',function(ev){
var ev = event||window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase()=='li'){
alert('li');
}
},false)
注意:
Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,真正点击的元素;
target.nodeName:对象的标签名,但是得到的是大写字母;
toLowerCase():使用该方法即转化为小写;
这样就只点击li时弹出,,点击ul时不弹出;
且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知
上面说的是每个li元素操作的内容相同,但如果点击 每个元素操作的内容不同呢:
<div class="input_box">
<input type="button" value="增加" id="a"/>
<input type="button" value="删除" id="b"/>
<input type="button" value="修改" id="c"/>
</div>
</body>
<script>
var inputbox=document.getElementsByClassName('input_box')[0];
var a=document.getElementById('a');
var b=document.getElementById('b');
var c=document.getElementById('c');
inputbox.addEventListener("click",function(e){
var event=event||window.event;
var target=event.target||event.srcElement;
if(target.nodeName.toLowerCase()=='input'){
switch (target.id){
case "a":alert("a");break;
case "b":alert("b");break;
case "c":alert("c");break;
}
}
},false);
结果是点击a弹a;点b弹b;点c弹c;
现在讲的都是document加载完成的现有dom节点下的操作,那么如果是新增的节点呢
且看如下例子:
<input type="button" id="add" value="增加li"/>
<ul id="ul">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>
var add=document.getElementById('add');
var ul=document.getElementById('ul');
var li=document.getElementsByTagName('li');
ul.addEventListener('click',function(ev){
var event=event||window.event;
var target=event.target||event.srcElement;
if(target.nodeName.toLowerCase()=="li"){
for(var i=0;i<li.length;i++){
if(li[i]===target){
alert('这是第'+(i+1)+"个li");
}
}
}
},false);
add.addEventListener('click',function(){
var new_li=document.createElement('li');
new_li.innerHTML='new li';
ul.appendChild(new_li);
},false)
用事件委托的方式,新添加的子元素是带有事件效果的,,点击增加按钮,,页面新添加了一个li,,,,点击新添加的li,,同样弹出。
当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。
总结:
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。