mass Framework event模块

此模块此用于屏蔽DOM事件的浏览器差异的,并提供强大的事件代理机制(delegate,undelegate,live,die):




//==========================================

//  事件模块 by 司徒正美 2011.8.21

//==========================================



(function(global,DOC){

    var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')];

    dom.define("event", "emitter,travel",function(){

        dom.log("加载dom.event模块成功")

        var types = "contextmenu,click,dblclick,mouseout,mouseover,mouseenter,mouseleave,mousemove,mousedown,mouseup,mousewheel," +

        "abort,error,load,unload,resize,scroll,change,input,select,reset,submit,"+"blur,focus,focusin,focusout,"+"keypress,keydown,keyup";

        dom.eventSupport = function( eventName,el ) {

            el = el || DOC.createElement("div");

            eventName = "on" + eventName;

            var ret = eventName in el;

            if (el.setAttribute && !ret ) {

                el.setAttribute(eventName, "return;");

                ret = typeof el[eventName] === "function";

            }

            el = null;

            return ret;

        };

        var events = dom.events, specials = events.special, rword = dom.rword;

        //用于在标准浏览器下模拟mouseenter与mouseleave

        //现在除了IE系列支持mouseenter/mouseleave/focusin/focusout外

        //opera11也支持这四个事件,同时它们也成为w3c DOM3 Event的规范

        //详见http://www.filehippo.com/pl/download_opera/changelog/9476/

        //http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html

        var brokenMouseEnter = !dom.eventSupport("mouseenter");

        "mouseenter_mouseover,mouseleave_mouseout".replace(rword,function(types){

            types = types.split("_");

            var obj = specials[ types[0] ]  = {

                setup:function(target){

                    dom.bind(target, types[1],function(){

                        obj[uuid(target)+"_handle"] = arguments.callee;

                        e = events.fix(e);

                        var parent = e.relatedTarget;

                        try {

                            while ( parent && parent !== target ) {

                                parent = parent.parentNode;

                            }

                            if ( parent !== target ) {

                                e.type = types[0];

                                events.handle.call(target,e);

                            }

                        } catch(e) { };

                    });

                },

                teardown :function(target){

                    events.teardown(target, types[1],obj[uuid(target)+"_handle"]);

                    delete obj[uuid(target)+"_handle"]

                }

            };

            obj.liveSetup = obj.setup;

            obj.liveTeardown = obj.teardown;

            if(!brokenMouseEnter){

                delete obj.setup;

                delete obj.teardown;

            }

        });



        if(!dom.eventSupport("focusin")){

            "focusin_focus,focusout_blur".replace(rword,function(types){

                types = types.split("_");

                var attaches = 0;

                function handler(e) {

                    events.fire(e.target, types[0]);

                }

                specials[  types[0] ]  = {

                    type: types[1],

                    setup:function(){

                        if ( attaches++ === 0 ) {

                            DOC.addEventListener(  types[1], handler, true );

                        }

                    },

                    teardown: function() {

                        if ( --attaches === 0 ) {

                            DOC.removeEventListener( types[1], handler, true );

                        }

                    }

                }

            });

        }

        //http://www.w3help.org/zh-cn/causes/SD9013

        //https://prototype.lighthouseapp.com/projects/8886/tickets/697-eventfire-to-support-all-events-not-only-custom-ones

        //http://www.quirksmode.org/dom/events/scroll.html

        if(!dom.eventSupport("mousewheel")){

            specials.mousewheel = {

                type    : "DOMMouseScroll"//FF

            }

        }

        //取得事件发送器的uniqueID

        function uuid(target){

            return dom.data(target,"uuid")

        }

        //submit事件的冒泡情况----IE6-9 :form ;FF: document; chrome: window;safari:window;opera:window

        if(!dom.eventSupport("submit") && !DOC.dispatchEvent ){

            var submitCode = dom.oneObject("13,108");

            var submitButton = dom.oneObject("submit,image");

            var submitInput = dom.oneObject("text,password,textarea");

            var submitRoom = specials.submit = {

                liveSetup: function(target){

                    //将事件回调函数保存到一个在卸载时可以找到它的地方

                    submitRoom[uuid(target)+"_handle"] =  function(){

                        var e = events.fix(event), el = e.target, type = el.type;

                        if( el.form &&  ( submitButton[type] || submitCode[ e.which ] && submitInput[type]) ){

                            dom.log("模拟IE下的submit事件冒泡 ");

                            e.type = "submit";

                            events.handle.call(target, e);

                        }

                    }

                    "onclick,onkeypress".replace(rword,function(type){

                        target.attachEvent(type ,submitRoom[uuid(target)+"_handle"] );

                    })

                },

                liveTeardown: function(target){

                    "onclick,onkeypress".replace(rword,function(type){

                        target.detachEvent(type ,submitRoom[uuid(target)+"_handle"]||dom.noop );

                    });

                    delete submitRoom[uuid(target)+"_handle"];

                }

            }

        }

        //reset事件的冒泡情况----FF与opera能冒泡到document,其他浏览器只能到form

        if(!dom.eventSupport("reset") ){

            var resetRoom = specials.reset = {

                liveSetup:DOC.dispatchEvent ? 0 : function(target){

                    resetRoom[uuid(target)+"_handle"] =  function(){

                        var e = events.fix(event), el = e.target;

                        if(  el.form && (e.which === 27  ||  el.type == "reset") ){

                            dom.log("模拟reset事件冒泡 ");

                            e.type = "reset";

                            events.handle.call(target, e);

                        }

                    }

                    "onclick,onkeypress".replace(rword,function(type){

                        target.attachEvent(type , resetRoom[uuid(target)+"_handle"]);

                    });

                },

                liveTeardown:DOC.dispatchEvent ? 0 : function(target){

                    "onclick,onkeypress".replace(rword,function(type){

                        target.dettachEvent(type ,resetRoom[uuid(target)+"_handle"]);

                    })

                    delete resetRoom[uuid(target)+"_handle"];

                }

            }

        }

        //change事件的冒泡情况----FF与opera能冒泡到document,其他浏览器完全不冒泡

        if(!dom.eventSupport("change") ){

            function getVal( node ) {

                var type = node.type, val = node.value;

                if ( type === "radio" || type === "checkbox" ) {

                    val = node.checked;

                } else if ( type === "select-multiple" ) {

                    val = node.selectedIndex === -1 ? "":

                    dom.lang( node.options).map(function( node ) {

                        return node.selected;

                    }).join("-") ;

                } else if ( type === "select-one" ) {

                    val = node.selectedIndex;

                }

                return val;

            }

            function changeHandle( e ) {

                var  eType = e.type,node = e.srcElement,nType = node.type

                //如果不是表单元素,又或者处于只读状态下,直接返回

                if ( !node.form || node.readOnly ) {

                    return;

                }

                if ( eType === "keydown" ) {

                    var flag = false;

                    if ( (e.keyCode === 13 && nType !== "textarea"  ) ||

                        (e.keyCode === 32 && ( nType === "checkbox" || nType === "radio")) ||

                        nType === "select-multiple" ) {

                        flag = true;

                    }

                    if(!flag){

                        return;

                    }

                }

                //取得之前的数据

                var oldData = dom.data( node, "_change_data" );

                //获取现在的数据

                var newData = getVal(node);

                // focusout是不用重设数据的

                if ( eType !== "focusout" || nType !== "radio" ) {

                    dom.data( node, "_change_data", newData );

                }

                if ( newData === undefined || newData === oldData ) {

                    return;

                }

                if ( oldData != null || newData ) {

                    e = events.fix(e);

                    e.type = "change";

                    events.handle.call(node, e);

                }

            }





            var changeRoom = specials.change = {



                liveSetup: function(node){

                    //此事件用于收集数据

                    node.attachEvent("onbeforeactivate", function( ) {

                        changeRoom[uuid(node)+event.type+"_handler"] = arguments.callee;

                        dom.data( node, "_change_data", getVal(node) );

                    });

                    ////当点击目标元素时,再点击页面的其他位置时,依次会发生如下事件beforeactivate  click | beforedeactivate focusout

                    "onfocusout,onbeforedeactivate,onclick,onkeydown".replace(rword,function(type){

                        node.attachEvent(type ,function(){

                            changeRoom[uuid(node)+event.type+"_handler"] = arguments.callee;

                            changeHandle.call(node, event);

                        });

                    });

                },

                liveTeardown:function(node){

                    var uid = uuid(node);

                    "focusout,beforedeactivate,click,keydown,beforeactivate".replace(rword,function(type){

                        node.detachEvent("on"+type,changeRoom[uid+type+"_handler"]||dom.noop );

                        delete changeRoom[uid+type+"_handler"]

                    });

                }

            }

        }



        //当一个元素,或者其内部任何一个元素获得焦点的时候会触发这个事件。

        //这跟focus事件区别在于,他可以在父元素上检测子元素获取焦点的情况。

        //=================================

        dom.include({

            toggleClick:function(/*fn1,fn2,fn3*/){

                var fns = [].slice.call(arguments), i = 0;

                return this.click(function(e){

                    var fn  = fns[i++] || fns[i = 0, i++];

                    fn.call(this,e);

                })

            },

            hover: function( fnOver, fnOut ) {

                return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );

            }

        });

        "bind,unbind,fire".replace(rword, function(method){

            dom.fn[method] = function(){

                var args = arguments;

                return this.each(function(target){

                    events[method].apply(target, args);

                });

            }



        });

        //事件代理的原理就是把事件侦听器绑定在顶层元素上,通过判定下层冒泡或捕获上来的事件源元素的特定,执行相应的回调

        "delegate,undelegate".replace(rword,function(method){

            dom.fn[method] = function(expr,type,callback){

                var special = events.special[ type ] || {};

                var bag = {

                    live: expr,

                    type: type,

                    callback:callback

                }

                bag.uuid =  callback.uuid = dom.uuid++;

                //选取可用的事件类型,如mouseenter在标准浏览器下就不能用,需要用mouseover冒充

                type = special.type || type

                return this.each(function(node){

                    events[method === "delegate" ? "bind" : "unbind"].apply(node, [type, bag, true]);

                });

            }

        });

        "live,die".replace(rword,function(method){

            dom.fn[method] = function(type,callback){

                var expr = this.selector;

                var doc = this.doc || DOC;

                if(!expr){

                    expr = dom.noop;

                }

                return dom(doc)[method === "live" ? "delegate" : "undelegate"](expr, type, callback);

            }

        });

        types.replace(rword,function(type){

            dom.fn[type] = function(callback, phase, trust){

                return this.bind(type, callback, phase, trust);

            }

        });

    });

})(this,this.document);

相关链接:dom Framework emitter模块

你可能感兴趣的:(framework)