javascript事件系统

2117次阅读  |  发布于5年以前

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