Miloer の 闲言碎语

一个面试题引起的事件委托


(图片与本文无关)

起因

从掘金看到一篇文章破解前端面试(80% 应聘者不及格系列):从 DOM 说起
有题目,就顺着做了,然后自己稍加改动了一下,最后就索性当个例子,记录下来。

事件委托

什么是事件委托?

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

网上有这么个收快递的例子来讲解事件委托:
快递员把快递寄到公司,由前台代收,判断东西是谁的,然后分发给物主。
如果每个员工挨个接收的话,就影响了办公效率。

在程序里也是一样的,每个函数都是对象,都会占用内存,内存中的对象越多,性能就越差。如果使用事件委托,那么就只针对它的父级进行操作(利用冒泡事件)。

网上随便找了个例子,不使用事件委托的。

1
2
3
4
5
<ul id="myLinks">
<li id="goSomewhere" >Go somewhere</li>
<li id="doSomething" >Do something</li>
<li id="sayHi" >Say hi</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
var item1=document.getElementById("goSomewhere");
var item2=document.getElementById("doSomething");
var item3=document.getElementById("sayHi");
item1.onclick = function(){
console.log('goSomewhere');
}
item2.onclick = function(){
console.log('doSomething');
}
item3.onclick = function(){
alert("hello");
}

这个是顺着掘金文章写得一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<input type="button" name="" id="btn" value="add" />
<ul id="ndContainer">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
window.onload = function() {
var ndBtn = document.querySelector('#btn');
var ndContainer = document.querySelector('#ndContainer');
var ndLi = document.getElementsByTagName('li');
ndContainer.addEventListener('mouseover', function() {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName == "LI") {
alert(target.innerHTML);
} else {
alert(target.nodeName);
//mouseenter,不冒泡
}
})
ndBtn.addEventListener('click', function() {
var length = ndLi.length;
var createLi = document.createElement('li');
createLi.innerHTML = length + 1;
ndContainer.appendChild(createLi);
})
}
</script>
</body>
</html>

使用事件委托不用去挨个遍历元素的节点,通过target的属性判断元素,执行事件。

特意使用了moseenter这个不冒泡的方法测试了一下。

总结一下事件委托的优点吧:提高性能,提高代码质量,减少代码量,给别人看的时候也会很舒服。

其他

顺着这个例子再说说别的,如果你细心的话你应该看到

1
2
3
4
5
var ndBtn = document.querySelector('#btn');
var ndContainer = document.querySelector('#ndContainer');
var ndLi = document.getElementsByTagName('li');

为什么 ndLi 不用querySelectorAll来获取?
先来看看他们是什么?

1
2
3
4
5
6
7
var ndLi = document.getElementsByTagName('li');
var testLi = document.querySelectorAll('li');
console.log("ByTagName" + ndLi);
console.log("querySelectorAll" + testLi);
ByTagName[object HTMLCollection]
querySelectorAll[object NodeList]

console.log里为什么没加toString方法是因为“前面字符串+“导致隐式类型转换了,不明白的可以戳这

一个是object HTMLCollection 一个是object NodeList

objectNodeList-The NodeList object returned by the querySelectorAll() method must be static ([DOM], section 8).

它返回的是一个静态的NodelIst对象。
如果使用这个add方法下 就一直是4 ,4, 4。

object HTMLCollection-An HTMLCollection is a list of nodes. An individual node may be accessed by either ordinal index or the node’s name or id attributes.
Note: Collections in the HTML DOM are assumed to be live meaning that they are automatically updated when the underlying document is changed.

而HTMLCollection是一个动态的元素集合,每次文档每次改变时都会重新对文档进行查询。

最后

有些东西写的不够详细,包括最后的其他,如果感兴趣想了解的朋友自己搜一下。

如果有不对的地方,烦请指正。