2005年的时候,jQuery的作者John Resig提出了一个兼容IE和标准浏览器的addEvent的方案,在当时认为是最佳的方案,但是几天后Dean Edwards提出了更好的方案,John Resig之后把Dean Edwards的方法应用在了jQuery中(在jQuery的事件系统的注释代码中可以看到Dean Edwards的署名)。
jQuery历经了这么多的版本,在原来Dean Edwards的基础上,John Resig对于最初的addEvent的方法有了一定的改进。最近在深入学习javascript的事件系统时,看了不少的jQuery的代码,我自己也写了一个简单的javascript事件系统,也算是对于jQuery事件的一个精简。当然这里指得精简并不是说jQuery代码比较臃肿,而是jQuery作为一个类库,需要考虑的东西有很多,也紧密结合了选择器,所以代码量确实很庞大。我的javascript事件系统解决了几个最基本的常见问题:
1. 尽量避免IE在绑定事件时出现的内存泄漏;
2. 修正了IE的this指向问题;
3. 让IE可以支持一些常见的事件标准方法;
4. 同类型的事件可以多次绑定并按顺序执行;
(function( win, undefined ){
var easyEvent = function(){
// 用来存储数据的全局对象
var cacheData = {
/* 1 : {
* eclick : [ handler1, handler2, handler3 ];
* clickHandler : function(){ //... };
* } */
},
uuid = 1,
expando = 'cache' + ( +new Date() + "" ).slice( -8 ); // 生成随机数
var base = {
// 设置、返回缓存的数据
// 关于缓存系统详见:http://stylechen.com/cachedata.html
data : function( elem, val, data ){
var index = elem === win ? 0 :
elem.nodeType === 9 ? 1 :
elem[expando] ? elem[expando] :
(elem[expando] = ++uuid),
thisCache = cacheData[index] ?
cacheData[index] :
( cacheData[index] = {} );
if( data !== undefined ){
// 将数据存入缓存中
thisCache[val] = data;
}
// 返回DOM元素存储的数据
return thisCache[val];
},
// 删除缓存
removeData : function( elem, val ){
var index = elem === win ? 0 :
elem.nodeType === 9 ? 1 :
elem[expando];
if( index === undefined ) return;
// 检测对象是否为空
var isEmptyObject = function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
// 删除DOM元素所有的缓存数据
delteProp = function(){
delete cacheData[index];
if( index <= 1 ) return;
try{
// IE8及标准浏览器可以直接使用delete来删除属性
delete elem[expando];
}
catch ( e ) {
// IE6/IE7使用removeAttribute方法来删除属性(document会报错)
elem.removeAttribute( expando );
}
};
if( val ){
// 只删除指定的数据
delete cacheData[index][val];
if( isEmptyObject( cacheData[index] ) ){
delteProp();
}
}
else{
delteProp();
}
},
// 绑定事件
addEvent : function( elem, type, handler ){
if( elem.addEventListener ){
elem.addEventListener( type, handler, false );
}
else if( elem.attachEvent ){
elem.attachEvent( 'on' + type, handler );
}
},
// 删除事件
removeEvent : function( elem, type, handler ){
if( elem.addEventListener ){
elem.removeEventListener( type, handler, false );
}
else if( elem.attachEvent ){
elem.detachEvent( 'on' + type, handler );
}
},
// 修复IE浏览器支持常见的标准事件的API
fixEvent : function( e ){
// 支持DOM 2级标准事件的浏览器无需做修复
if ( e.target ){
return e;
}
var event = {}, name;
event.target = e.srcElement || document;
event.preventDefault = function(){
e.returnValue = false;
};
event.stopPropagation = function(){
e.cancelBubble = true;
};
// IE6/7/8在原生的win.event中直接写入自定义属性
// 会导致内存泄漏,所以采用复制的方式
for( name in e ){
event[name] = e[name];
}
return event;
},
// 依次执行事件绑定的函数
eventHandler : function( elem ){
return function( event ){
event = $.fixEvent( event || win.event );
var type = event.type,
events = $.data( elem, 'e' + type );
for( var i = 0, handler; handler = events[i++]; ){
if( handler.call(elem, event) === false ){
event.preventDefault();
event.stopPropagation();
}
}
console.debug( event );
}
}
};
var $ = base;
var extend = {
bind : function( elem, type, handler ){
var events = $.data( elem, 'e' + type ) || $.data( elem, 'e' + type, [] );
// 将事件函数添加到缓存中
events.push( handler );
// 同一事件类型只注册一次事件,防止重复注册
if( events.length === 1 ){
var eventHandler = $.eventHandler( elem );
$.data( elem, type + 'Handler', eventHandler );
$.addEvent( elem, type, eventHandler );
}
//console.debug( cacheData );
},
unbind : function( elem, type, handler ){
var events = $.data( elem, 'e' + type );
if( !events ) return;
// 如果没有传入要删除的函数则删除该事件类型的缓存
if( !handler ){
events = undefined;
}
// 如果有具体的函数则只删除一个
else{
for( var i = events.length - 1; i >= 0; i-- ){
var fn = events[i];
if( fn === handler ){
events.splice( i, 1 );
}
}
}
// 删除事件和缓存
if( !events || !events.length ){
var eventHandler = $.data( elem, type + 'Handler' );
$.removeEvent( elem, type, eventHandler );
$.removeData( elem, type + 'Handler' );
$.removeData( elem, 'e' + type );
}
}
};
return extend;
};
win.easyEvent = easyEvent();
})( window, undefined );
调用:
javascript事件系统
<script src="event.js" type="text/javascript"></script>
<script type="text/javascript">
var box = document.getElementById( 'box' ),
btn = document.getElementById( 'btn' );
var getNodeName = function( e ){
alert( e.target.nodeName ); // DIV
};
easyEvent.bind( box, 'click', function(){
alert( this.innerHTML ); // javascript事件系统
});
easyEvent.bind( box, 'click', getNodeName );
// 只删除一个指定的事件函数
easyEvent.bind( btn, 'click', function(){
easyEvent.unbind( box, 'click', getNodeName );
});
// 删除所有该事件类型的事件函数
easyEvent.bind( btn, 'click', function(){
easyEvent.unbind( box, 'click' );
});
</script>
原载于:雨夜带刀's Blog
本文链接:http://stylechen.com/easyevent.html
如需转载请以链接形式注明原载或原文地址。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8