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 $;
    })
    
    机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
  • 相关阅读:
    Roce ofed 环境搭建与测试
    Ubuntu 1804 搭建NFS服务器
    Redhat 8.0.0 安装与网络配置
    Centos 8.1 安装与网络配置
    SUSE 15.1 系统安装
    VSpare ESXi 7.0 基本使用(模板、iso、SRIOV)
    VSpare ESXi 7.0 服务器安装
    open SUSE leap 15.1 安装图解
    KVM虚拟机网卡连接网桥
    GitHub Action一键部署配置,值得拥有
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2853152.html
Copyright © 2011-2022 走看看