一般的事件检查代码如下:
function addHandler(target, eventType, handler) { if (target.addEventListener) { target.addEventListener(eventType, handler, false); } else { target.attachEvent("on" + eventType, handler); } } function removeHandler(target, eventType, handler) { if (target.removeEventListener) { target.removeEventListener(eventType, handler, false); } else { target.detachEvent("on" + eventType, handler); } }乍一看,这些函数为实现他们的目的已经足够优化。影藏的性能问题在于每次函数调用时都执行重复工作。每次都进行同样的检查,看看某种方法是否存在。如果addHandler
以上连就调用addEventListener那么每个后续调用都要出现这句代码。
解决方案一:延迟加载:
function addHandler(target, eventType, handler) { if (target.addEventListener) { addHandler = function (target, eventType, handler) { target.addEventListener(eventType, handler, false); }; } else { addHandler = function (target, eventType, handler) { target.attachEvent("on" + eventType, handler); }; } addHandler(target, eventType, handler); } function removeHandler(target, eventType, handler) { if (target.removeEventListener) { removeHandler = function (target, eventType, handler) { target.removeEventListener(eventType, handler, false); }; } else { removeHandler = function (target, eventType, handler) { target.detachEvent("on" + eventType, handler); }; } removeHandler(target, eventType, handler); }这两个方法第一次被调用时,检查一次并决定适用那种方法附加或分离句柄。然后,原始函数就被包含适当操作的新函数覆盖了。最后调用新函数并将原始参数传给他。
调用一个延迟加载函数总是在第一次使用较长时间,因为它必须运行监测然后调用另一个函数已完成任务。但是,后续调用同一函数将快很多,因为不再执行检查逻辑了。
延迟加载适用于函数不会在页面上立即被用到的场合。
解决方案二:预加载
var addHandler = document.body.addEventListener ? function (target, eventType, handler) { target.addEventListener(eventType, handler, false); } : function (target, eventType, handler) { target.attachEvent("on" + eventType, handler); }; var removeHandler = document.body.removeEventListener ? function (target, eventType, handler) { target.removeEventListener(eventType, handler, false); } : function (target, eventType, handler) { target.detachEvent("on" + eventType, handler); };它在脚本加载前进行检查,而不等待函数调用。这样做检查仍然只是一次,但此过程中来的更早。预加载适用于一个函数马上就会用到,而且在整个页面生命周期中经常使用的场合
在这里强调一下2级事件虽然支持事件捕获和事件冒泡,但是addEventListener、removeEventListener中第三个参数为 false表示支持事件冒泡,为true表示支持事件捕获。同时这里也把Dean Edwards写的addEvent库的代码给出来一共大家参考
function addEvent(element, type, handler) { if (!handler.$$guid) { //为每个事件处理函数赋予一个独立的ID handler.$$guid = addEvent.guid++; } if (!element.events) { //为元素建立一个事件类型散列表 element.events = {}; } //为每个元素、事件建立一个事件处理函数散列表 var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; if (element["on" + type]) { handlers[0] = element["on" + type]; } } handlers[handler.$$guid] = handler; element["on" + type] = handleEvent; } //创建独立ID的计数器 addEvent.guid = 1; function removeEvent(element, type, handler) { if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; } } function handleEvent(event) { var returnValue = true; event = event || fixEvent(window.event);//获取事件对象 var handlers = this.events[event.type];//获取书剑处理函数散列表引用 for (var i in handlers) { this.$$handleEvent = handlers[i]; if (this.$$handleEvent(event) === false) { returnValue = false; } } return returnValue; }; function fixEvent(event) { event.preventDefault = fixEvent.preventDefault; event.stopPropagation = fixEvent.stopPropagation; return event; }; fixEvent.preventDefault = function () { this.returnValue = false; }; fixEvent.stopPropagation = function () { this.cancelBubble = true; } /*调用示例 addEvent(document.getElementsByTagName("div")[0], "click", function () { alert("ok"); removeEvent(this, "click", arguments.callee); }); */