在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
事件处理程序来移除掉所有的事件处理程序,和上一个方法联系一下,假如用了事件委托来添加事件处理程序,那么在这个时候去移除所有的事件处理程序就会简单很多.