zoukankan      html  css  js  c++  java
  • mass Framework event模块 v9

    本次升级借鉴了jQuery事件模块的许多代码,可谓是jQuery事件模块的改良版。

    与原先一样,拆分为两块,event模块是支持新一代的浏览器的,如IE9,chrome5+, opera10+,safari5+;event_fix是对付IE678。

    拆分后的好处,在标准浏览器中,我们就不要加载这么多代码,跑这么多注定要跳过的分支,有效地提升性能。

    拆分后就有利于我对标准浏览器有一个新的了解,发现firefox成为最拖后腿的一位。它在滚轮事件,focusin, focusout的迟迟不合作,让我们不得不奠出eventSupport等利器。webkit系还需要模拟mouseenter, mouseleave事件。由于标准浏览器的原生属性不能被覆盖,比如我们用mouseover来冒充mouseenter,那么我们还得将它外包一层,这工作为$.Event来做。它本来就是用于摒蔽事件对象在各浏览器的差异性的,让IE也拥有W3C的调用接口。因此$.Event还是不能移到event_fix模块。

    在jQuery中还有一个simulate方法,用于让事件对象伪装成另一类事件,根据最后一个参数在本层或整个DOM树中传播。但通过研读源码发现,它的最后参数总是true,并且它也多用于修复IE的事件派发,只有一处是用于FF。因此我把它放到event_fix中去了。

    在$.event.fix方法中,我发现jQuery对原事件对象的属性复制是限死的,规定好了某些属性要被复制,因此伪事件对象在一些场合还是要访问originalEvent来干活。这里我做了一些改良。并且针对鼠标事件与键盘事件这两类事件的大补丁,我也分配好它们的归属。一个目标,减少无效的分支判定。jQuery在dispatch这个方法中实在做了许多判定了,因此跑得很慢,这会在一些持续触发的事件,如scroll, resize, mousemove等非常吃力。

    即使在event模块中,事件系统还是要对一些事件进行特殊处理,分别是load, focus, blur, click, beforeunload。

    再来看event_fix,只要是处理IE的事件代理,change与submit,还有就是事件对象对标准的跟随。这补丁模块总归要入土的,但现在它对大陆人来说还是必不可少。

    事件模块是使用wrap方式进行,完全伪装原短对象在DOM的触发行为。许多神一样的代码是jQuery团队写的,偶只是照搬。另一些奇技淫巧虽然是我自创的,但给出足够的链接希望你们能看得懂。这一个框架最重要也是最复杂的一部分,许多框架的事件系统能搞成这样具够扩展性也极让人困惑。

    event.js

    //=========================================
    // 事件系统 v9
    //==========================================
    define("event", top.dispatchEvent ? ["$node"] : ["$event_fix"], function($) {
        var facade = $.event || ($.event = {
            //对某种事件类型进行特殊处理
            special: {},
            //对Mouse事件这一大类事件类型的事件对象进行特殊处理
            fixMouse: function(event, real) {
                if(event.type === "mousewheel") { //处理滚轮事件
                    if("wheelDelta" in real) { //统一为±120,其中正数表示为向上滚动,负数表示向下滚动
                        // http://www.w3help.org/zh-cn/causes/SD9015
                        var delta = real.wheelDelta
                        //opera 9x系列的滚动方向与IE保持一致,10后修正
                        if(window.opera && opera.version() < 10) delta = -delta;
                        event.wheelDelta = Math.round(delta); //修正safari的浮点 bug
                    } else if("detail" in real) {
                        event.wheelDelta = -real.detail * 40; //修正FF的detail 为更大众化的wheelDelta
                    }
                }
            }
        }),
            eventHooks = facade.special,
            rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
            rtypenamespace = /^([^.]*)(?:\.(.+)|)$/,
            mouseEvents = "contextmenu,click,dblclick,mouseout,mouseover,mouseenter,mouseleave,mousemove,mousedown,mouseup,mousewheel,",
            eventMap = $.oneObject(mouseEvents, "Mouse"),
            types = mouseEvents + ",keypress,keydown,keyup," + "blur,focus,focusin,focusout," + "abort,error,load,unload,resize,scroll,change,input,select,reset,submit" //input
            $.eventSupport = function(eventName, el) {
                el = el || document.createElement("div");
                eventName = "on" + eventName;
                var ret = eventName in el;
                if(el.setAttribute && !ret) {
                    el.setAttribute(eventName, "");
                    ret = typeof el[eventName] === "function";
                    el.removeAttribute(eventName);
                }
                el = null;
                return ret;
            };
    
        function Event(src, props) {
            if(!(this instanceof $.Event)) {
                return new Event(src, props);
            }
            this.originalEvent = {}; //保存原生事件对象
            if(src && src.type) {
                this.originalEvent = src; //重写
                this.type = src.type;
            } else {
                this.type = src;
            }
            this.defaultPrevented = false;
            if(props) {
                $.mix(this, props);
            }
            this.timeStamp = new Date - 0;
        };
        Event.prototype = {
            toString: function() {
                return "[object Event]"
            },
            preventDefault: function() { //阻止默认行为
                this.defaultPrevented = true;
                var e = this.originalEvent
                if(e && e.preventDefault) {
                    e.preventDefault();
                }
                e.returnValue = false;
                return this;
            },
            stopPropagation: function() { //阻止事件在DOM树中的传播
                var e = this.originalEvent
                if(e && e.stopPropagation) {
                    e.stopPropagation();
                } //propagationStopped的命名出自 http://opera.im/kb/userjs/
                e.cancelBubble = this.propagationStopped = true;
                return this;
            },
            stopImmediatePropagation: function() { //阻止事件在一个元素的同种事件的回调中传播
                this.isImmediatePropagationStopped = true;
                this.stopPropagation();
                return this;
            }
        }
        $.Event = Event;
        $.mix(eventHooks, {
            load: { //此事件不能冒泡
                noBubble: true
            },
            click: { //处理checkbox中的点击事件
                trigger: function() {
                    if(this.nodeName == "INPUT" && this.type === "checkbox" && this.click) {
                        this.click();
                        return false;
                    }
                }
            },
            focus: { //IE9-在不能聚焦到隐藏元素上,强制触发此事件会抛错
                trigger: function() {
                    if(this !== document.activeElement && this.focus) {
    
                        try {
                            this.focus();
                            return false;
                        } catch(e) {}
                    }
                },
                delegateType: "focusin"
            },
            blur: {
                trigger: function() { //blur事件的派发使用原生方法实现
                    if(this === document.activeElement && this.blur) {
                        this.blur();
                        return false;
                    }
                },
                delegateType: "focusout"
            },
            beforeunload: {
                postDispatch: function(event) {
                    if(event.result !== void 0) {
                        event.originalEvent.returnValue = event.result;
                    }
                }
            }
        });
    
        $.mix(facade, {
            //addEventListner API的支持情况:chrome 1+ FF1.6+ IE9+ opera 7+ safari 1+;
            //http://functionsource.com/post/addeventlistener-all-the-way-back-to-ie-6
            add: function(elem, hash) {
                var elemData = $._data(elem),
                    //取得对应的缓存体
                    types = hash.type,
                    //原有的事件类型,可能是复数个
                    selector = hash.selector,
                    //是否使用事件代理
                    handler = hash.handler; //回调函数
                if(elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler) {
                    return;
                }
                hash.uniqueNumber = $.getUid(handler); //确保hash.uuid与fn.uuid一致
                var events = elemData.events || (elemData.events = []),
                    eventHandle = elemData.handle;
                if(!eventHandle) {
                    elemData.handle = eventHandle = function(e) {
                        return typeof $ !== "undefined" && (!e || facade.triggered !== e.type) ? facade.dispatch.apply(eventHandle.elem, arguments) : void 0;
                    };
                    eventHandle.elem = elem; //由于IE的attachEvent回调中的this不指向绑定元素,需要强制缓存它
                }
    
                types.replace($.rword, function(t) {
                    var tns = rtypenamespace.exec(t) || [],
                        type = tns[1];
                    var namespaces = (tns[2] || "").split(".").sort();
                    // 看需不需要特殊处理
                    var hook = eventHooks[type] || {};
                    // 事件代理与事件绑定可以使用不同的冒充事件
                    type = (selector ? hook.delegateType : hook.bindType) || type;
                    hook = eventHooks[type] || {};
                    var handleObj = $.mix({}, hash, {
                        type: type,
                        origType: tns[1],
                        namespace: namespaces.join(".")
                    });
    
                    var handlers = events[type]; //初始化事件列队
                    if(!handlers) {
                        handlers = events[type] = [];
                        handlers.delegateCount = 0;
                        if(!hook.setup || hook.setup.call(elem, namespaces, eventHandle) === false) {
                            if($["@bind"] in elem) {
                                $.bind(elem, type, eventHandle)
                            }
                        }
                    }
                    if(hook.add) {
                        hook.add.call(elem, handleObj);
                    }
                    //先处理用事件代理的回调,再处理用普通方式绑定的回调
                    if(selector) {
                        handlers.splice(handlers.delegateCount++, 0, handleObj);
                    } else {
                        handlers.push(handleObj);
                    }
                    //用于优化fire方法
                    facade.global[type] = true;
                })
                //防止IE内在泄漏
                elem = null;
            },
            //用于优化事件派发
            global: {},
            //移除目标元素绑定的回调
            remove: function(elem, hash) {
                var elemData = $._data(elem),
                    events, origType
                if(!(events = elemData.events)) return;
    
                var types = hash.type || "",
                    selector = hash.selector,
                    handler = hash.handler;
                types.replace($.rword, function(t) {
                    var tns = rtypenamespace.exec(t) || [],
                        type = origType = tns[1],
                        namespaces = tns[2];
                    //只传入命名空间,不传入事件类型,则尝试遍历所有事件类型
                    if(!type) {
                        for(type in events) {
                            facade.unbind(elem, $.mix({}, hash, {
                                type: type + t
                            }));
                        }
                        return
                    }
                    var hook = eventHooks[type] || {};
                    type = (selector ? hook.delegateType : hook.bindType) || type;
                    var handlers = events[type] || [];
                    var origCount = handlers.length;
                    namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
    
                    for(var j = 0, handleObj; j < handlers.length; j++) {
                        handleObj = handlers[j];
                        //如果事件类型相同,回调相同,命名空间相同,选择器相同则移除此handleObj
                        if((origType === handleObj.origType) && (!handler || handler.uniqueNumber === handleObj.uniqueNumber) && (!namespaces || namespaces.test(handleObj.namespace)) && (!selector || selector === handleObj.selector || selector === "**" && handleObj.selector)) {
                            handlers.splice(j--, 1);
    
                            if(handleObj.selector) {
                                handlers.delegateCount--;
                            }
                            if(hook.remove) {
                                hook.remove.call(elem, handleObj);
                            }
                        }
                    }
    
                    if(handlers.length === 0 && origCount !== handlers.length) {
                        if(!hook.teardown || hook.teardown.call(elem, namespaces, elemData.handle) === false) {
                            if($["@bind"] in elem) {
                                $.unbind(elem, type, elemData.handle)
                            }
                        }
    
                        delete events[type];
                    }
                })
    
                if($.isEmptyObject(events)) {
                    delete elemData.handle;
                    $._removeData(elem, "events"); //这里会尝试移除缓存体
                }
            },
            //通过传入事件类型或事件对象,触发事件回调,在整个DOM树中执行
            trigger: function(event) {
                var elem = this;
                //跳过文本节点与注释节点,主要是照顾旧式IE
                if(elem && (elem.nodeType === 3 || elem.nodeType === 8)) {
                    return;
                }
    
                var i, cur, old, ontype, handle, eventPath, bubbleType, type = event.type || event,
                    namespaces = event.namespace ? event.namespace.split(".") : [];
    
                // focus/blur morphs to focusin/out; ensure we're not firing them right now
                if(rfocusMorph.test(type + facade.triggered)) {
                    return;
                }
    
                if(type.indexOf(".") >= 0) {
                    //分解出命名空间
                    namespaces = type.split(".");
                    type = namespaces.shift();
                    namespaces.sort();
                }
                //如果从来没有绑定过此种事件,也不用继续执行了
                if(!elem && !facade.global[type]) {
                    return;
                }
    
                // Caller can pass in an Event, Object, or just an event type string
                event = typeof event === "object" ?
                // 如果是$.Event实例
                event.originalEvent ? event :
                // Object literal
                new $.Event(type, event) :
                // Just the event type (string)
                new $.Event(type);
    
                event.type = type;
                event.isTrigger = true;
                event.namespace = namespaces.join(".");
                event.namespace_re = event.namespace ? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
                ontype = type.indexOf(":") < 0 ? "on" + type : "";
                //清除result,方便重用
                event.result = void 0;
                if(!event.target) {
                    event.target = elem;
                }
                //取得额外的参数
                var data = $.slice(arguments);
                data[0] = event;
                //判定是否需要用到事件冒充
                var hook = eventHooks[type] || {};
                if(hook.trigger && hook.trigger.apply(elem, data) === false) {
                    return;
                }
    
                //铺设往上冒泡的路径,每小段都包括处理对象与事件类型
                eventPath = [
                    [elem, hook.bindType || type]
                ];
                if(!hook.noBubble && !$.type(elem, "Window")) {
    
                    bubbleType = hook.delegateType || type;
                    cur = rfocusMorph.test(bubbleType + type) ? elem : elem.parentNode;
                    for(old = elem; cur; cur = cur.parentNode) {
                        eventPath.push([cur, bubbleType]);
                        old = cur;
                    }
                    //一直冒泡到window
                    if(old === (elem.ownerDocument || document)) {
                        eventPath.push([old.defaultView || old.parentWindow || window, bubbleType]);
                    }
                }
    
                //沿着之前铺好的路触发事件
                for(i = 0; i < eventPath.length && !event.propagationStopped; i++) {
    
                    cur = eventPath[i][0];
                    event.type = eventPath[i][1];
    
                    handle = ($._data(cur, "events") || {})[event.type] && $._data(cur, "handle");
                    if(handle) {
                        handle.apply(cur, data);
                    }
                    //处理直接写在标签中的内联事件或DOM0事件
                    handle = ontype && cur[ontype];
                    if(handle && handle.apply && handle.apply(cur, data) === false) {
                        event.preventDefault();
                    }
                }
                event.type = type;
                //如果没有阻止默认行为
                if(!event.defaultPrevented) {
    
                    if((!hook._default || hook._default.apply(elem.ownerDocument, data) === false) && !(type === "click" && elem.nodeName == "A")) {
                        if(ontype && $.isFunction(elem[type]) && elem.nodeType) {
    
                            old = elem[ontype];
    
                            if(old) {
                                elem[ontype] = null;
                            }
                            //防止二次trigger,elem.click会再次触发addEventListener中绑定的事件
                            facade.triggered = type;
                            try {
                                //IE6-8在触发隐藏元素的focus/blur事件时会抛出异常
                                elem[type]();
                            } catch(e) {}
                            delete facade.triggered;
    
                            if(old) {
                                elem[ontype] = old;
                            }
                        }
                    }
                }
    
                return event.result;
            },
            //执行用户回调,只在当前元素中执行
            dispatch: function(e) {
                //如果不存在事件回调就没有必要继续进行下去
                var eventType = e.type,
                    handlers = (($._data(this, "events") || {})[eventType] || [])
                    if(!handlers.length) {
                        return;
                    }
                    //摒蔽事件对象在各浏览器下的差异性
                var event = $.event.fix(e),
                    delegateCount = handlers.delegateCount,
                    args = $.slice(arguments),
                    hook = eventHooks[eventType] || {},
                    handlerQueue = [],
                    ret, selMatch, matched, matches, handleObj, sel
                    //重置第一个参数
                    args[0] = event;
                event.delegateTarget = this;
    
                // 经典的AOP模式
                if(hook.preDispatch && hook.preDispatch.call(this, event) === false) {
                    return;
                }
                //收集阶段
                //如果使用了事件代理,则先执行事件代理的回调, FF的右键会触发点击事件,与标签不符
                if(delegateCount && !(event.button && eventType === "click")) {
                    for(var cur = event.target; cur != this; cur = cur.parentNode || this) {
                        //disabled元素不能触发点击事件
                        if(cur.disabled !== true || eventType !== "click") {
                            selMatch = {};
                            matches = [];
                            for(var i = 0; i < delegateCount; i++) {
                                handleObj = handlers[i];
                                sel = handleObj.selector;
                                //判定目标元素(this)的孩子(cur)是否匹配(sel)
                                if(selMatch[sel] === void 0) {
                                    selMatch[sel] = $(sel, this).index(cur) >= 0
                                }
                                if(selMatch[sel]) {
                                    matches.push(handleObj);
                                }
                            }
                            if(matches.length) {
                                handlerQueue.push({
                                    elem: cur,
                                    matches: matches
                                });
                            }
                        }
                    }
                }
    
                // 这是事件绑定的回调
                if(handlers.length > delegateCount) {
                    handlerQueue.push({
                        elem: this,
                        matches: handlers.slice(delegateCount)
                    });
                }
    
                // 如果没有阻止事件传播,则执行它们
                for(i = 0; i < handlerQueue.length && !event.propagationStopped; i++) {
                    matched = handlerQueue[i];
                    event.currentTarget = matched.elem;
                    for(var j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped; j++) {
                        handleObj = matched.matches[j];
                        //namespace,.namespace_re属性只出现在trigger方法中
                        if(!event.namespace || event.namespace_re && event.namespace_re.test(handleObj.namespace)) {
    
                            event.data = handleObj.data;
                            event.handleObj = handleObj;
                            ret = ((eventHooks[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args);
                            handleObj.times--;
                            if(handleObj.times === 0) { //如果有次数限制并到用光所有次数,则移除它
                                facade.unbind(matched.elem, handleObj)
                            }
                            if(ret !== void 0) {
                                event.result = ret;
                                if(ret === false) {
                                    event.preventDefault();
                                    event.stopPropagation();
                                }
                            }
                        }
                    }
                }
    
                if(hook.postDispatch) {
                    hook.postDispatch.call(this, event);
                }
    
                return event.result;
            },
    
            //修正事件对象,摒蔽差异性
            fix: function(event) {
                if(!event.originalEvent) {
                    var real = event;
                    event = $.Event(real);
                    //复制真实事件对象的成员
                    for(var p in real) {
                        if(!(p in event)) {
                            event[p] = real[p]
                        }
                    }
                    //如果不存在target属性,为它添加一个
                    if(!event.target) {
                        event.target = event.srcElement || document;
                    }
                    //safari的事件源对象可能为文本节点,应代入其父节点
                    if(event.target.nodeType === 3) {
                        event.target = event.target.parentNode;
                    }
                    event.metaKey = !! event.ctrlKey; // 处理IE678的组合键
                    var callback = facade["fix" + eventMap[event.type]]
                    if(typeof callback == "function") {
                        callback(event, real)
                    }
                }
                return event;
            }
        });
        facade.bind = facade.add;
        facade.unbind = facade.remove;
        //以下是用户使用的API
        $.implement({
            hover: function(fnIn, fnOut) {
                return this.mouseenter(fnIn).mouseleave(fnOut || fnIn);
            },
            delegate: function(selector, types, fn, times) {
                return this.on(types, selector, fn, times);
            },
            live: function(types, fn, times) {
                $.log("$.fn.live() is deprecated")
                $(this.ownerDocument).on(types, this.selector, fn, times);
                return this;
            },
            one: function(types, fn) {
                return this.on(types, fn, 1);
            },
            undelegate: function(selector, types, fn) { /*顺序不能乱*/
                return arguments.length == 1 ? this.off(selector, "**") : this.off(types, fn, selector);
            },
            die: function(types, fn) {
                $.log("$.fn.die() is deprecated")
                $(this.ownerDocument).off(types, fn, this.selector || "**", fn);
                return this;
            },
            fire: function() {
                var args = arguments;
                return this.each(function() {
                    facade.trigger.apply(this, args);
                });
            }
        });
        //这个迭代器产生四个重要的事件绑定API on off bind unbind
        var rtypes = /^[a-z0-9_\-\.\s\,]+$/i
        "on_bind,off_unbind".replace($.rmapper, function(_, method, mapper) {
            $.fn[method] = function(types, selector, fn) {
                if(typeof types === "object") {
                    for(var type in types) {
                        $.fn[method](this, type, selector, types[type], fn);
                    }
                    return this;
                }
                var hash = {};
                for(var i = 0; i < arguments.length; i++) {
                    var el = arguments[i];
                    if(typeof el == "number") {
                        hash.times = el;
                    } else if(typeof el == "function") {
                        hash.handler = el
                    }
                    if(typeof el === "string") {
                        if(hash.type != null) {
                            hash.selector = el.trim();
                        } else {
                            hash.type = el.trim(); //只能为字母数字-_.空格
                            if(!rtypes.test(hash.type)) {
                                throw new Error("事件类型格式不正确")
                            }
                        }
                    }
                }
                if(!hash.type) {
                    throw new Error("必须指明事件类型")
                }
                if(method === "on" && !hash.handler) {
                    throw new Error("必须指明事件回调")
                }
                hash.times = hash.times > 0 ? hash.times : Infinity;
                return this.each(function() {
                    facade[mapper](this, hash);
                });
            }
            $.fn[mapper] = function() { // $.fn.bind $.fn.unbind
                return $.fn[method].apply(this, arguments);
            }
        });
    
        types.replace($.rword, function(type) { //这里产生以事件名命名的快捷方法
            eventMap[type] = eventMap[type] || (/key/.test(type) ? "Keyboard" : "HTML")
            $.fn[type] = function(callback) {
                return callback ? this.bind(type, callback) : this.fire(type);
            }
        });
        /* mouseenter/mouseleave/focusin/focusout已为标准事件,经测试IE5+,opera11,FF10+都支持它们
    详见http://www.filehippo.com/pl/download_opera/changelog/9476/
             */
        if(!+"\v1" || !$.eventSupport("mouseenter")) { //IE6789不能实现捕获与safari chrome不支持
            "mouseenter_mouseover,mouseleave_mouseout".replace($.rmapper, function(_, type, fix) {
                eventHooks[type] = {
                    delegateType: fix,
                    bindType: fix,
                    handle: function(event) {
                        var ret, target = this,
                            related = event.relatedTarget,
                            handleObj = event.handleObj;
                        // For mousenter/leave call the handler if related is outside the target.
                        // NB: No relatedTarget if the mouse left/entered the browser window
                        if(!related || (related !== target && !$.contains(target, related))) {
                            event.type = handleObj.origType;
                            ret = handleObj.handler.apply(this, arguments);
                            event.type = fix;
                        }
                        return ret;
                    }
                }
            });
        }
        //现在只有firefox不支持focusin,focusout事件,并且它也不支持DOMFocusIn,DOMFocusOut,不能像DOMMouseScroll那样简单冒充,Firefox 17+
        if(!$.support.focusin) {
            "focusin_focus,focusout_blur".replace($.rmapper, function(_, orig, fix) {
                var attaches = 0,
                    handler = function(event) {
                        event = facade.fix(event);
                        $.mix(event, {
                            type: orig,
                            isSimulated: true
                        });
                        facade.trigger.call(event.target, event);
                    };
                eventHooks[orig] = {
                    setup: function() {
                        if(attaches++ === 0) {
                            document.addEventListener(fix, handler, true);
                        }
                    },
                    teardown: function() {
                        if(--attaches === 0) {
                            document.removeEventListener(fix, handler, true);
                        }
                    }
                };
            });
        }
        try {
            //FF需要用DOMMouseScroll事件模拟mousewheel事件
            document.createEvent("MouseScrollEvents");
            eventHooks.mousewheel = {
                bindType: "DOMMouseScroll",
                delegateType: "DOMMouseScroll"
            }
            if($.eventSupport("mousewheel")) {
                delete eventHooks.mousewheel;
            }
        } catch(e) {};
    
        return $;
    })
    

    event_fix.js

    define("event_fix", !! document.dispatchEvent, ["$node"], function($) {
        //模拟IE678的reset,submit,change的事件代理
        var rformElems = /^(?:input|select|textarea)$/i
        var facade = $.event = {
            special: {},
            fixMouse: function(event) {
                // 处理鼠标事件 http://www.w3help.org/zh-cn/causes/BX9008
                var doc = event.target.ownerDocument || document; //safari与chrome下,滚动条,视窗相关的东西是放在body上
                var box = document.compatMode == "BackCompat" ? doc.body : doc.documentElement
                event.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0);
                event.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0);
                //如果不存在relatedTarget属性,为它添加一个
                if(!event.relatedTarget && event.fromElement) { //mouseover mouseout
                    event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
                }
                //标准浏览判定按下鼠标哪个键,左1中2右3
                var button = event.button
                //IE event.button的意义 0:没有键被按下 1:按下左键 2:按下右键 3:左键与右键同时被按下 4:按下中键 5:左键与中键同时被按下 6:中键与右键同时被按下 7:三个键同时被按下
                event.which = [0, 1, 3, 0, 2, 0, 0, 0][button]; //0现在代表没有意义
            },
            fixKeyboard: function(event) {
                event.which = event.charCode != null ? event.charCode : event.keyCode;
            }
        };
        var special = facade.special
    
        function simulate(type, elem, event) {
            event = new $.Event(event);
            $.mix({
                type: type,
                isSimulated: true
            });
            $.event.trigger.call(elem, event);
            if(event.defaultPrevented) {
                event.preventDefault();
            }
        }
        special.change = {
            setup: function() {
                if(rformElems.test(this.nodeName)) {
                    // IE doesn't fire change on a check/radio until blur; trigger it on click
                    // after a propertychange. Eat the blur-change in special.change.handle.
                    // This still fires onchange a second time for check/radio after blur.
                    if(this.type === "checkbox" || this.type === "radio") {
                        $(this).bind("propertychange._change", function(event) {
                            if(event.originalEvent.propertyName === "checked") {
                                this._just_changed = true;
                            }
                        });
                        $(this).bind("click._change", function(event) {
                            if(this._just_changed && !event.isTrigger) {
                                this._just_changed = false;
                            }
                            // Allow triggered, simulated change events (#11500)
                            simulate("change", this, event);
                        });
                    }
                    return false;
                }
                // Delegated event; lazy-add a change handler on descendant inputs
                $(this).bind("beforeactivate._change", function(e) {
                    var elem = e.target;
                    if(rformElems.test(elem.nodeName) && !$._data(elem, "_change_attached")) {
                        $(elem).bind("change._change", function(event) {
                            if(this.parentNode && !event.isSimulated && !event.isTrigger) {
                                simulate("change", this.parentNode, event);
                            }
                            $._data(elem, "_change_attached", true);
                        })
                    }
                });
            },
            handle: function(event) {
                var elem = event.target;
                // Swallow native change events from checkbox/radio, we already triggered them above
                if(this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox")) {
                    return event.handleObj.handler.apply(this, arguments);
                }
            },
            teardown: function() {
                facade.remove(this, "._change");
                return !rformElems.test(this.nodeName);
            }
        }
        special.submit = {
            setup: function() {
                // Only need this for delegated form submit events
                if(this.tagName === "FORM") {
                    return false;
                }
                // Lazy-add a submit handler when a descendant form may potentially be submitted
                $(this).bind("click._submit keypress._submit", function(e) {
                    // Node name check avoids a VML-related crash in IE (#9807)
                    var elem = e.target,
                    form = /input|button/i.test(elem.tagName) ? elem.form : undefined;
                    if(form && !$._data(form, "_submit_attached")) {
                        facade.bind(form, {
                            type: "submit._submit",
                            callback: function(event) {
                                event._submit_bubble = true;
                            }
                        });
                        $._data(form, "_submit_attached", true);
                    }
                });
            // return undefined since we don't need an event listener
            },
    
            postDispatch: function(event) {
                // If form was submitted by the user, bubble the event up the tree
                if(event._submit_bubble) {
                    delete event._submit_bubble;
                    if(this.parentNode && !event.isTrigger) {
                        simulate("submit", this.parentNode, event);
                    }
                }
            },
    
            teardown: function() {
                if(this.tagName == "FORM") {
                    return false;
                }
                facade.remove(this, "._submit");
            }
        }
        return $;
    })
    
    机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
  • 相关阅读:
    【腾讯Bugly干货分享】微信Tinker的一切都在这里,包括源码(一)
    【腾讯Bugly干货分享】iOS10 SiriKit QQ适配详解
    【腾讯Bugly干货分享】安卓单元测试:What, Why and How
    【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术
    【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法
    【腾讯Bugly经验分享】程序员的成长离不开哪些软技能?
    【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验
    从零开始安装Hadoop视频教程
    如何在MAC机器中实现移动设备WiFI上网(没有专门的无线路由器的情况)
    Alfresco安装与配置图解
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2853152.html
Copyright © 2011-2022 走看看