基本概念
事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素;
一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
举个例子,比如一个宿舍的同学同时快递到了,一种方法就是他们都傻傻地一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一一分发给每个宿舍同学;
事件冒泡与捕获

为什么用事件委托
1:绑定事件越多,浏览器内存占用越大,严重影响性能。
2:ajax的出现,局部刷新的盛行,导致每次加载完,都要重新绑定事件
3:部分浏览器移除元素时,绑定的事件并没有被及时移除,导致的内存泄漏,严重影响性能
4:大部分ajax局部刷新的,只是显示的数据,而操作却是大部分相同的,重复绑定,会导致代码的耦合性过大,严重影响后期的维护。
事件委托影响性能的因素:
- 元素中,绑定事件委托的次数;
- 点击的最底层元素,到绑定事件元素之间的DOM层数;
什么时候选择使用事件委托
1:只在必须的地方,使用事件委托,比如:ajax
的局部刷新区域
2:尽量的减少绑定的层级,不在body
元素上,进行绑定
3:减少绑定的次数,如果可以,那么把多个事件的绑定,合并到一次事件委托中去,由这个事件委托的回调,来进行分发。
一个例子
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul id="ul-test">
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
</body>
<script type="text/javascript">
var oUl=document.getElementById("ul-test");
var oLi=oUl.getElementsByTagName("li");
for(var i=0,len=oLi.length;i<len;i++){
oLi[i].addEventListener("click",function(){
alert(this.innerHTML)
})
}
</script>
</html>
问题在于:
- for循环,循环的是li,10个li就循环10次,绑定10次事件,1000个就循环了1000次,绑定1000次事件!
- 如果li不是本来就在页面上的,是未来元素,是页面加载了,再通过js动态加载进来了,上面的写法是无效的,点击li是没有反应的!应该怎么解决以上问题?
解决的办法就是通过事件委托
var oUl=document.getElementById("ul-test");
oUl.addEventListener("click",function(ev){
console.log(ev)
var ev=ev||window.event;// 兼容性处理
var target=ev.target||ev.srcElement;
// 获取到目标阶段指向的元素。如果点击的最底层是li元素
if(target.tagName.toLowerCase()==='li'){
alert(target.innerHTML)
}
})
原理
事件的传播有三个阶段:
当一个事件发生以后,它会在不同的DOM节点之间传播(propagation)。这种传播分成三个阶段:
- 第一阶段:从window对象传导到目标节点,称为“捕获阶段”(capture phase)。
- 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
- 第三阶段:从目标节点传导回window对象,称为“冒泡阶段”(bubbling phase)。
这种三阶段的传播模型,会使得一个事件在多个节点上触发。比如,假设点击<div>之中嵌套一个<p>节点。
<div>
<p>Click Me</p>
</div>
如果对这两个节点的click
事件都设定监听函数,则click
事件会被触发四次。
- 捕获阶段:事件从<div>向<p>传播时,触发<div>的click事件;
- 目标阶段:事件从<div>到达<p>时,触发<p>的click事件;
- 目标阶段:事件离开<p>时,触发<p>的click事件;
- 冒泡阶段:事件从<p>传回<div>时,再次触发<div>的click事件。
注意,用户点击网页的时候,浏览器总是假定click事件的目标节点,就是点击位置的嵌套最深的那个节点(嵌套在<div>节点的<p>节点)。所以,<p>节点的捕获阶段和冒泡阶段,都会显示为target阶段。
事件传播的最上层对象是window
,接着依次是document
,html(document.documentElement)
和body(document.body)
。也就是说,如果<body>
元素中有一个<div>
元素,点击该元素。事件的传播顺序,在捕获阶段依次为window、document、html、body、div
,在冒泡阶段依次为div、body、html、document、window。
定义
由于事件会在冒泡阶段传向父节点,因此可以把子节点的监听函数绑定在父节点上,用父节点的监听函数统一处理多个子节点的事件,这就是事件代理。
var ul = document.querySelector('ul');
ul.addEventListener('click', function(event) {
if (event.target.tagName.toLowerCase() === 'li') {
// some code
}
});
上面这个函数事件监听绑在ul身上,但是监听到点击li的事件,然后处理li点击后的回调。
就算li是动态添加进来的依然能够监听到。
总结
由于一个事件在传播的过程中有三个阶段,由最外层的父节点一层层传递到最里面的子节点,先是捕获阶段,到目标阶段,再到冒泡阶段。如果我们想监听到动态添加的子节点的事件,我们可以把事件绑在父节点上,利用event.target,筛选到子节点,处理事件相应的回调!!!
网友评论