myEvent.js javascript跨浏览器事件框架

event究竟有多么复杂?可见前辈的6年前的努力:最佳的addEvent是怎样诞生的,后起之秀jQuery也付出了一千六百多行血汗代码(v 1.5.1)搞定了6年后出现的各种核的浏览器。

我参考前辈的代码以及自己的理解尝试写了一个事件框架,我的框架完成了一个事件机制的核心,它能提供统一接口实现多事件绑定以及避免内存泄漏等其他一些问题,更重要的是性能还不错。

我的手法:

所有回调函数根据元素、事件类型、回调函数唯一ID缓存在一个_create对象中(其内部具体结构可见下面源码的关于_cache的注释)。
事件绑定使用一个_create代理函数处理,并且一个元素的各类型事件全部通过此进行分发,同时运用apply方法让IE的指针指向元素。
通过数组队列解决IE回调函数执行顺序的问题。
fix函数将处理回调函数传入的event参数以及其他兼容问题。此处参考了jQuery.event.fix。
断开事件与元素的循环引用避免内存泄漏。
一、核心实现:

复制代码 代码如下:


// myEvent 0.2
// 2011.04.06 - TangBin - planeart.cn - MIT Licensed
/**
* 事件框架
* @namespace
* @see ?p=1285
*/
var myEvent = (function () {
var _fid = 1,
_guid = 1,
_time = (new Date).getTime(),
_nEid = '{$eid}' + _time,
_nFid = '{$fid}' + _time,
_DOM = document.addEventListener,
_noop = function () {},
_create = function (guid) {
return function (event) {
event = api.fix(event || window.event);
var i = 0,
type = (event || (event = document.event)).type,
elem = _cache[guid].elem,
data = arguments,
events = _cache[guid].events[type];
for (; i < events.length; i ++) {
if (events[i].apply(elem, data) === false) event.preventDefault();
};
};
},
_cache = {/*
1: {
elem: (HTMLElement),
events: {
click: [(Function), (..)],
(..)
},
listener: (Function)
},
(..)
*/};
var api = {
/**
* 事件绑定
* @param {HTMLElement} 元素
* @param {String} 事件名
* @param {Function} 要绑定的函数
*/
bind: function (elem, type, callback) {
var guid = elem[_nEid] || (elem[_nEid] = _guid ++);
if (!_cache[guid]) _cache[guid] = {
elem: elem,
listener: _create(guid),
events: {}
};
if (type && !_cache[guid].events[type]) {
_cache[guid].events[type] = [];
api.add(elem, type, _cache[guid].listener);
};
if (callback) {
if (!callback[_nFid]) callback[_nFid] = _fid ++;
_cache[guid].events[type].push(callback);
} else
return _cache[guid];
},
/**
* 事件卸载
* @param {HTMLElement} 元素
* @param {String} 事件名
* @param {Function} 要卸载的函数
*/
unbind: function (elem, type, callback) {
var events, i, list,
guid = elem[_nEid],
handler = _cache[guid];
if (!handler) return;
events = handler.events;
if (callback) {
list = events[type];
if (!list) return;
for (i = 0; i < list.length; i ++) {
list[i][_nFid] === callback[_nFid] && list.splice(i--, 1);
};
if (list.length === 0) return api.unbind(elem, type);
} else if (type) {
delete events[type];
api.remove(elem, type, handler.listener);
} else {
for (i in events) {
api.remove(elem, i, handler.listener);
};
delete _cache[guid];
};
},
/**
* 事件触发 (注意:不会触发浏览器默认行为与冒泡)
* @param {HTMLElement} 元素
* @param {String} 事件名
* @param {Array} (可选)附加数据
*/
triggerHandler: function (elem, type, data) {
var guid = elem[_nEid],
event = {
type: type,
target: elem,
currentTarget: elem,
preventDefault: _noop,
stopPropagation: _noop
};
data = data || [];
data.unshift(event);
guid && _cache[guid].listener.apply(elem, data);
try {
elem['on' + type] && elem['on' + type].apply(elem, data);
//elem[type] && elem[type]();
} catch (e) {};
},
// 原生事件绑定接口
add: _DOM ? function (elem, type, listener) {
elem.addEventListener(type, listener, false);
} : function (elem, type, listener) {
elem.attachEvent('on' + type, listener);
},
// 原生事件卸载接口
remove: _DOM ? function (elem, type, listener) {
elem.removeEventListener(type, listener, false);
} : function (elem, type, listener) {
elem.detachEvent('on' + type, listener);
},
// 修正
fix: function (event) {
if (_DOM) return event;
var name,
newEvent = {},
doc = document.documentElement,
body = document.body;
newEvent.target = event.srcElement || document;
newEvent.target.nodeType === 3 && (newEvent.target = newEvent.target.parentNode);
newEvent.preventDefault = function () {event.returnValue = false};
newEvent.stopPropagation = function () {event.cancelBubble = true};
newEvent.pageX = newEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
newEvent.pageY = newEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
newEvent.relatedTarget = event.fromElement === newEvent.target ? event.toElement : event.fromElement;
// !!IE写event会极其容易导致内存泄漏,Firefox写event会报错
// 拷贝event
for (name in event) newEvent[name] = event[name];
return newEvent;
}
};
return api;
})();


我给一万个元素绑定事件进行了测试,测试工具为sIEve,结果:
事件框架   耗时   内存  
IE8  
jQuery.bind   1064 MS   79.80 MB  
myEvent.bind   623 MS   35.82 MB  
IE6  
jQuery.bind   2503 MS   74.23 MB  
myEvent.bind   1810 MS   28.48 MB  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdjxzw.html