在JavaScript中,添加到頁面上的事件處理程序數量將會直接關系到頁面的整體運行性能,主要有一下幾方面的原因
- 每個函數都是對象,都會占用內存,對象越多,性能越差
- 必須實現指定所有事件處理程序而導致的
DOM
訪問次數,會延遲整個頁面的交互就緒時間
要如何提升性能呢
-
方案一:事件委托
事件委托利用了事件冒泡,只為一個元素(DOM樹中盡量高層次的元素)指定一個事件處理程序,就可以管理某一類型的所有事件
舉個例子,對于click事件,加入有多個元素都有該事件,不用事件委托我們會這么做,為每個元素逐一添加事件處理程序,當元素過多而且又不是調用同一處理函數時是非常麻煩的
//html
<body>
<div id="eles">
<button id="btn1">1</button>
<button id="btn2">2</button>
<button id="btn3">3</button>
<button id="btn4">4</button>
</div>
//JavaScript
window.onload = function () {
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var btn3 = document.getElementById('btn3');
var btn4 = document.getElementById('btn4');
var EventUtil = {
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false)
}
else if (element.attachEvent) {
element.attachEvent('on' + type, handler)
}
else {
element['on' + type] = handler;
}
}
};
EventUtil.addHandler(btn1, 'click', handler);
EventUtil.addHandler(btn2, 'click', handler);
EventUtil.addHandler(btn3, 'click', handler);
EventUtil.addHandler(btn4, 'click', handler);
};
function handler(event) {
console.log(event.target);
}
那么,利用事件委托我們可以像下邊這樣做
//JavaScript
window.onload = function () {
var eles = document.getElementById('eles');
var EventUtil = {
addHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false)
}
else if (element.attachEvent) {
element.attachEvent('on' + type, handler)
}
else {
element['on' + type] = handler;
}
},
getEvent: function (event) {
return event ? event : window.event.srcElement
},
getTarget: function (event) {
return event.target || event.srcElement
}
};
EventUtil.addHandler(eles, 'click', handler);
function handler(event) {
var event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var eleId = target.id;
switch (eleId) {
case 'btn1':
console.log('1');
break;
case 'btn2':
console.log('2');
break;
case 'btn3':
console.log('3');
break;
case 'btn4':
console.log('4');
break;
default:
console.log('err');
}
}
};
在這個代碼中我們為四個按鈕的父級元素添加了點擊事件,然后在事件處理程序中再去區分是哪一個按鈕,這樣做的好處是我們只去了一次DOM節點,也只添加了一次處理程序,占用內存更少,速度更快
如果可行,我們也可以為document元素添加事件處理程序來處理某一類型的多個事件,在事件發生元素多次數多的情況下事件委托是非常好的選擇,適合用事件委托技術的事件有click
,mousedown
,mouseup
,keydown
,keyup
和keypress
總結一下事件委托的優勢
- document對象很快就可以訪問,而且可以再頁面生命周期的任何時間點上為它添加事件處理程序(只要可單擊的元素呈現在頁面上,就可以立即具備適當的功能)
- 在頁面上設置事件處理程序所需的更時間少(所需DOM引用少)
- 整個頁面占用的內存更少
方案二:移除事件處理程序
有時候我們會通過innerHtml
來替換某個元素的子元素,但假如這個子元素此時綁定著一些事件處理程序呢,假如直接替換,會導致原來的子元素雖然被移走,但是事件處理程序任然與子元素保持著引用關系,這個時候我們就需要在用innerHTML
移除元素之前先移除它的事件處理程序,具體方法可看移除事件處理程序
還有一種情況就是卸載頁面的時候,如果在頁面卸載之前沒有處理干凈事件處理程序,那么它們就會留在內存中,不斷地跳轉或者刷新頁面會導致內存中滯留的對象數目越來越多,最好的做法就是在頁面卸載之前通過unload
事件處理程序來移除掉所有的事件處理程序,和上一個方法聯系一下,假如用了事件委托來添加事件處理程序,那么在這個時候去移除所有的事件處理程序就會簡單很多.