事件委托,说白了就是本来你要给一个元素添加一个事件(比如点击),出于某些考率,现在要把事件添加给它的外层元素。
事件源
解释事件委托之前,应该先了解下事件源。简单来说,事件源就是直接触发事件的元素。在事件函数中可通过event对象的target属性访问事件源。
看一个例子:
图中,单击span部分,控制台会打印出对应span元素;单击span外的p部分,会打印出对应p元素;单击p外的div部分,会打印出对应div元素。

关键代码如下:
1 <body>
2 <div id="box">div
3 <p>p
4 <span>span</span>
5 </p>
6 </div>
7 </body>
8 <script>
9 window.onload=function(){
10 var box=document.getElementById("box");
11 box.onclick=function(ev){
12 console.log((ev || window.event).target);
13 };
14 </script>
事件委托
再说事件委托。通常,我们用事件委托解决两问题:1、避免不必要的性能开销;2 、新生成的元素不需要重新添加事件。
先看一个经典的例子:
例子中,我们希望点击li能打印对应的文本。
1 <body>
2 <ul id="list">
3 <li>red</li>
4 <li>green</li>
5 <li>blue</li>
6 <li>pink</li>
7 <li>black</li>
8 </ul>
9 </body>
10 <script>
11 var list=document.getElementById("list");
12 var lis=document.querySelectorAll("li");
13
14 for(var i=0;i<lis.length;i++){
15 lis[i].onclick=function(){
16 console.log(this.firstChild.nodeValue); //本来单击li,可以打印出其对应文本
17 };
18 }
19
20 list.innerHTML+='<li>yellow</li>'; //这里加了一行后,单击li就无法打印出li对应文本了
21 </script>
本来,单击li是可以在控制台打印对应文体的。但是,现在20行加了一行代码,单击li就无法打印出li对应文本了。因为innerHTML会将原有内容全部清空,再赋予新内容,所以li绑定的单击事件也将失效。类似这种情况的还有,使用cloneNode(),新节点不会复制源节点的事件;jQuery中通过remove()移除的节点,重新添加回来后,之前绑定的事件将失效。(PS:detach()在这点上与remove()不同。)
下面,我将上面代码中的for循环换成以下代码:
1 list.onclick = function(ev){
2 var ev = ev || window.event;
3 if(ev.target.tagName.toLowerCase()=='li'){
4 console.log(ev.target.innerHTML); //单击li,即事件源是li是打印出其对应文本
5 }
6 };
这样就解决了刚才遇到的问题,而且避免了使用for循环。其实,在这个解决方案中,我们是把点击事件绑定在了li的父级元素ul身上,也就是所谓的事件委托。然后对事件源进行判断,等于间接地给li添加了点击事件。
本作品采用知识共享署名 4.0 国际许可协议进行许可。