JavaScript事件委托
概述:
又名事件代理,利用事件冒泡,只指定一个事件处理程序,管理某一类的所有事件。即把原本需要绑定在子元素的响应事件委托给父元素,让父元素进行事件监听。总的来说它就是DOM元素的事件冒泡。
举例:
取快递事件,学生个体进行网购,在收快递时将收快递这个行为委托给楼姨,让楼姨帮忙代收,楼姨收到快递后,集中处理分发或是代为付款。这样减少了大量事件和人力,不然学生就得挨个站在门口进行等待。
好处
js中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断与dom节点进行交互,访问dom次数越多,引起的浏览器重绘与重排的次数就越多,这样会延长整个页面的交互就绪时间,这就是为什么优化性能需要减少dom操作。使用事件委托将操作放到js程序里,与dom的操作只需要交互一次,并且只需要一个内存空间,这样就能大大提高性能。
原理
利用事件冒泡实现。比如点击给最里面的事件加一个click点击事件,这个事件就一层一层按顺序往外执行,这样的话给最外层的div加上点击事件,最终由于冒泡都会触发点击事件,这样将事件委托给最外层的父元素,这样就叫做事件委托,当新增子对象时无须进行再次绑定。
实现
事件相同
<div id="oBtn">
<ul id="ull">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
</div>
<script>
//未利用事件委托
window.onloade=function(){
var oUl =document.getElementsById("ull");
var aLi = oUl.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = function(){
alert(123);
}
}
}
//利用事件委托
window.onload = function(){
var oUl = document.getElementById("ull");
oUl.onclick = function(){
alert(123);
};
//当需要添加新节点且需要新节点也进行事件委托
var oBtn = document.getElementById("btn");
oBtn.onclick = function(){
num++;
var oLi = document.createElement('li');
oLi.innerHTML = 111*num;
oUl.appendChild(oLi);
};//由此可知,当需要给新增节点增加事件效果时,不需要去遍历只需要给腹肌元素添加事件即可
}
</script>
上述例子利用冒泡事件,将点击事件委托,当点击父元素中的任意点击之后就会触发,但是要是只想点击li时触发,这时就用到target了。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);
}
}
}
事件不同
<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>
<script>
//未使用事件委托
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('选择');
}
}
//使用事件委托
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;
}
}
}
}
</script>
要是li里面的内容各不相同,解决方案:实际上就是一个递归调用。
var oUl = document.getElementById('test');
oUl.addEventListener('click',function(ev){
var target = ev.target;
while(target !== oUl ){
if(target.tagName.toLowerCase() == 'li'){
console.log('li click~');
break;
}
target = target.parentNode;
}
})
适合用事件委托的事件
click,mousedown,mouseup,keydown,keyup,keypress。
值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。
不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。