javascript事件系统
by admin
at 2011-07-31 12:21:18
original http://stylechen.com/easyevent.html
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>