事件委托,顧名思義,就是將本來需要 A 處理的事情,委托給 B 來處理。在 JavaScript 中的事件委托又稱事件代理,事件委托就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。當然,如果子元素阻止了事件冒泡,那么委托也就沒法實現了。
如何舉個例子形容呢:
有三個同事預計會在周一收到快遞。為簽收快遞,有兩種辦法:一是三個人在公司門口等快遞;二是委托給前台 MM 代為簽收。現實當中,我們大都采用委托的方案(公司也不會容忍那么多員工站在門口就為了等快遞)。前台 MM 收到快遞后,她會判斷收件人是誰,然后按照收件人的要求簽收,甚至代為付款。這種方案還有一個優勢,那就是即使公司里來了新員工(不管多少),前台 MM 也會在收到寄給新員工的快遞后核實並代為簽收。
這里其實還有 2 層意思的:
第一,現在委托前台的同事是可以代為簽收的,即程序中的現有的 dom 節點是有事件的;
第二,新員工也是可以被前台 MM 代為簽收的,即程序中新添加的 dom 節點也是有事件的。
下面我們舉一個代碼例子:
我們要實現一個需求,當鼠標懸浮在 li 元素上,li 元素背景變成紅色,離開時,去掉背景。代碼如下:
<ul id="ul">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
window.onload = function() {
var oUl = document.getElementById('ul');
var aLi = oUl.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
aLi[i].onmouseover = function() {
this.style.background = 'red';
};
aLi[i].onmouseout = function() {
this.style.background = '';
};
}
};
我們循環遍歷每一個 li 元素,並為之加上 mouserover 和 mouseout 事件。這只適用於 li 元素特別少的情況,如果 li 元素特別多,定會引起性能問題。還有一個問題,如果這時候動態添加了一個 li 元素,這個元素是沒法響應事件的,因為沒有給他綁定任何事件。
通過事件委托實現
window.onload=function(){
var oUl = document.getElementById('ul');
oUl.onmouseover = function(e){
var e = e || window.event;
var target = e.target || e.srcElement;
if(target.nodeName.toLowerCase() == "li"){
target.style.background = "red";
}
}
oUl.onmouseout = function(e){
var e = e || window.event;
var target = e.target || e.srcElement;
if(target.nodeName.toLowerCase() == "li"){
target.style.background = "";
}
}
}
}
在這段代碼中,通過 event.target 來實現事件委托。這種方法避免了循環,提高了性能。
標准瀏覽器用 event.target,IE 瀏覽器用 event.srcElement
新問題,如果 li 還包含了其他元素怎么辦呢,這樣 target 就不是 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;
}
});
另外一個例子:
<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>
不用事件委托:
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;
}
}
};
};