文章出處

  jQ的一個個版本事系統都在修正著bug和不斷優化, 而且看了事件系統對事件的兼容更加熟悉, 更加了解jQ內部的事件機制。

  因為jQ對事件系統引入了事件命名空間,事件的代理, 事件的手動觸發,事件描述等等各種概念, 對事件的可操控性大大增加, 這個也是庫存在的意義, 不是說只要處理addEventListener和attachEvent可以做到的;在大型的項目中事件系統也可以作為發布者和派發者,對整個系統進行充分的解耦

  這些做為自己的筆記,一步一步走, 只是大概看了看, 還有不懂的地方, 最好的學習源代碼的方式不是看別人給我們說, 也不是看別人寫的書(參考是可以得), 自己動手是永恒不變的真理, 加油, 現在jQ的代碼分析都爛大街了,估計就我自己看看或者說是復習復習了。。。。

    var rnamespaces = /\.(.*)$/,
        rformElems = /^(?:textarea|input|select)$/i,
        rperiod = /\./g,
        rspace = / /g,
        rescape = /[^\w\s.|`]/g,
        fcleanup = function( nm ) {
            return nm.replace(rescape, "\\$&");
        },
        eventKey = "events";
        /*
        知識點匹配需要轉義的字符;
             rescape = /[^\w\s.|`]/g,
             //為每一個需要轉義的字符添加\
             nm.replace(rescape, "\\$&");
             fcleanup("sdfsdfdsfds.s&**(((*)f\\")
             "sdfsdfdsfds.s\&\*\*\(\(\(\*\)f\\"
         */
    /*
     * A number of helper functions used for managing events.
     * Many of the ideas behind this code originated from
     * Dean Edwards' addEvent library.
     */
    jQuery.event = {
        // Bind an event to an element
        // Original by Dean Edwards
        //所有綁定事件都是通過add這個方法綁定的;
                        //元素
                              //click mouseover 正常情況下;

                              //  如果是代理的情況下; add會綁定兩次,第一次是綁定live事件,第二個是綁定click事件; 第一個點以后的是要匹配的元素選擇器(要把 ^替換成.);
                              //"live.click.div"
                              // "live.click.`hehe"  ==>> click.`hehe;
                              // live.click.div`hehe  ==>> "click.div`hehe";
                                              //data沒毛用;
        add: function( elem, types, handler, data ) {
            //debugger;
            //text節點和 comment節點全滾哇, 話說 屬性節點(nodeType === 2)的你可以綁定事件嗎? 是的,好像真的可以哇, 奇葩了;
            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                return;
            };

            // For whatever reason, IE has trouble passing the window object
            // around, causing it to be cloned in the process
            // 跨iframe
            if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
                elem = window;
            }

            //綁定一個空的事件
            if ( handler === false ) {
                handler = returnFalse;
            } else if ( !handler ) {
                // Fixes bug #7229. Fix recommended by jdalton
                return;
            };

            var handleObjIn, handleObj;

            //根據傳進來的handler是否有handler屬性,然后設置handlerObjIn事件描述和,事件觸發要執行的函數handlerObjIn.handler;
            if ( handler.handler ) {
                handleObjIn = handler;
                handler = handleObjIn.handler;
            };

            // Make sure that the function being executed has a unique ID
            //保證了唯一的事件, 后面可以根據這個唯一的id進行remove或者別的操作,比較方便;
            if ( !handler.guid ) {
                handler.guid = jQuery.guid++;
            };

            // Init the element's event structure
            //這個獲取的是內部私用的緩存保存的數據;
            var elemData = jQuery._data( elem );

            // If no elemData is found then we must be trying to bind to one of the
            // banned noData elements
            // 沒有就不玩啦, 為什么會沒有呢 ,因為noData的元素不能亂給事件哇, object[classId="/\d/mig"], applet, embed;
            // 對啊embed不能綁定事件,只能通過innerHTML綁定事件, 以前碰到過這種情況;
            if ( !elemData ) {
                return;
            };
                                   //eventKey = "events" 上面定義了這個鳥東西;
            var events = elemData[ eventKey ],
                //eventHandle是為這個元素綁定的事件
                eventHandle = elemData.handle;

            //正常的events應該是一個數組哇, 是function的情況應該特別對待;
            if ( typeof events === "function" ) {
                // On plain objects events is a fn that holds the the data
                // which prevents this data from being JSON serialized
                // the function does not need to be called, it just contains the data
                eventHandle = events.handle;
                events = events.events;

            } else if ( !events ) {
                //處理非節點元素的事件綁定, 這個應該是為了擴張綁定事件到非節點元素上面;
                if ( !elem.nodeType ) {
                    // On plain objects, create a fn that acts as the holder
                    // of the values to avoid JSON serialization of event data
                    elemData[ eventKey ] = elemData = function(){};
                };

                //新建一個事件保存列表;
                elemData.events = events = {};
            };

            //所有的事件都綁定同一個事件函數, 剩下的給event.handle處理不同的情況;
            //使用這種方式對用戶的來說, 可配置性變好了, 比如
            // 1 : 你可以讓事件按照順序執行(某些瀏覽器不按照順序來,因為事件執行時冒泡階段執行);
            // 2 : 沒想出來;
            if ( !eventHandle ) {
                elemData.handle = eventHandle = function() {
                    // Handle the second event of a trigger and when
                    // an event is called after a page has unloaded
                                                                //jQuery.event.triggered默認是false的;
                    return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                        jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                        undefined;
                };
            };

            // Add elem as a property of the handle function
            // This is to prevent a memory leak with non-native events in IE.
            // 為事件函數添加元素的引用; 阻止ie下的內存泄漏;
            eventHandle.elem = elem;

            // Handle multiple events separated by a space
            // jQuery(...).bind("mouseover mouseout", fn);
            //開始了一大堆處理, 對綁定的事件進行c;
            types = types.split(" ");

            var type, i = 0, namespaces;

            while ( (type = types[ i++ ]) ) {
                //重新弄一個事件描述(引用);
                handleObj = handleObjIn ?
                    jQuery.extend({}, handleObjIn) :
                { handler: handler, data: data };

                // Namespaced event handlers
                // 修復時間的命名空間;
                // 目測現在事件代理被弄成 live  click^#div1^div的情況
                if ( type.indexOf(".") > -1 ) {
                    namespaces = type.split(".");
                    type = namespaces.shift();
                    handleObj.namespace = namespaces.slice(0).sort().join(".");
                } else {
                    namespaces = [];
                    handleObj.namespace = "";
                };

                //為事件描述添加事件類型
                handleObj.type = type;
                //為事件描述添加事件的guid, 這個handle是從bind那邊處理過的(處理了one,bind), 也可能從live那邊傳過來的;
                if ( !handleObj.guid ) {
                    handleObj.guid = handler.guid;
                }

                // Get the current list of functions bound to this event
                // 創建或者獲取事件的隊列;
                var handlers = events[ type ],
                    special = jQuery.event.special[ type ] || {};

                // Init the event handler queue
                if ( !handlers ) {
                    handlers = events[ type ] = [];

                    // Check for a special event handler
                    // Only use addEventListener/attachEvent if the special
                    // events handler returns false
                    // 如果綁定的是beforeunload,就特殊對待,
                    // //如果綁定focusin或者foucuseout就轉化成使用fouse和blur,
                    // live或者是
                    // 是ready就綁定到document.ready
                    // 如果是mouseenter或者是mouseleave,就使用mouseout和mousein模擬;
                    // live只有add和remove,所以這個setup肯定不走, 直接走addEVentListener的綁定;
                    if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
                        // Bind the global event handler to the element
                        //如果是live就是綁定了自定義事件, 觸發的時候要注意一下;
                        if ( elem.addEventListener ) {
                            elem.addEventListener( type, eventHandle, false );

                        } else if ( elem.attachEvent ) {
                            elem.attachEvent( "on" + type, eventHandle );
                        }
                    }
                }

                //使用事件代理的時候的卻有點繞, jQ高版本的話對事件代理進行了優化;;

                //live的時候,這里又綁定了一次哦,只有live有add和remove;
                if ( special.add ) {
                    //第一次add的是live的handlers,第二次add的才是真正的handlers;
                    //調用special的綁定方式進行綁定;
                    //這個綁定有重新迭代了一次$.event.add...所以要注意一下, 這一次的迭代才是真正綁定需要的事件
                    special.add.call( elem, handleObj );

                    if ( !handleObj.handler.guid ) {
                        handleObj.handler.guid = handler.guid;
                    };
                };
                //綁定事件的事件函數要做不同處理, 但是綁定的事件描述還是要根據事件的類型放到handlers里面去;

                //所有的處理都是為了把handleObj放到handlers這個對象里面;
                // Add the function to the element's handler list
                handlers.push( handleObj );

                //優化;
                // Keep track of which events have been used, for global triggering
                jQuery.event.global[ type ] = true;

                /*handle結構是這樣的
                $.cache = {
                    Number ElementGuid : {
                        string jQueryExpando : {
                            events : {
                                "click" : [function(){}, function(){}, function(){}, function(){}]
                            },
                            handle : function(){....}
                        }
                    }
                }
                */
            };

            // Nullify elem to prevent memory leaks in IE
            elem = null;
        },

        global: {},

        // Detach an event or set of events from an element
        //刪除事件目測應該和綁定差不多道理;
        remove: function( elem, types, handler, pos ) {
            // don't do events on text and comment nodes
            // 依舊可以把事件綁定給屬性節點;
            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                return;
            };

            if ( handler === false ) {
                handler = returnFalse;
            };

            //
            var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
                elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
                events = elemData && elemData[ eventKey ];

            //沒有事件列表還玩個毛;
            if ( !elemData || !events ) {
                return;
            };

            //這個和$.event.add一樣的,
            if ( typeof events === "function" ) {
                elemData = events;
                events = events.events;
            };

            //$.event.remove({type : "click",handler : clickFn});
            // types is actually an event object here
            if ( types && types.type ) {
                handler = types.handler;
                types = types.type;
            };

            // Unbind all events for the element
            // 沒有types的話, 就是移除所有的事件;
                                                        //類型是命名空間的話
            if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
                types = types || "";

                //迭代命名空間的事件,一個個刪除;
                for ( type in events ) {
                    jQuery.event.remove( elem, type + types );
                };
                //下面沒必要在走了;
                return;
            };

            // Handle multiple events separated by a space
            // jQuery(...).unbind("mouseover mouseout", fn);
            types = types.split(" ");

            while ( (type = types[ i++ ]) ) {
                origType = type;
                handleObj = null;
                all = type.indexOf(".") < 0;
                namespaces = [];

                //all 指是或否是這個事件的全部命名空間都要刪除;
                if ( !all ) {
                    // Namespaced event handlers
                    namespaces = type.split(".");
                    type = namespaces.shift();

                    namespace = new RegExp("(^|\\.)" +
                        jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
                };

                eventType = events[ type ];

                if ( !eventType ) {
                    continue;
                };

                if ( !handler ) {
                    for ( j = 0; j < eventType.length; j++ ) {
                        handleObj = eventType[ j ];
                        //對這個事件描述對象進行判斷, 如果匹配到了這個命名空間就把這個時間刪了;;
                        if ( all || namespace.test( handleObj.namespace ) ) {
                            jQuery.event.remove( elem, origType, handleObj.handler, j );
                            eventType.splice( j--, 1 );
                        }
                    }

                    continue;
                }

                special = jQuery.event.special[ type ] || {};

                for ( j = pos || 0; j < eventType.length; j++ ) {
                    handleObj = eventType[ j ];
                        //用戶也可以傳綁定的函數進來, 如果guid一樣就刪;
                    if ( handler.guid === handleObj.guid ) {
                        // remove the given handler for the given type
                        if ( all || namespace.test( handleObj.namespace ) ) {
                            if ( pos == null ) {
                                eventType.splice( j--, 1 );
                            };

                            //有remove的只有live有了;
                            if ( special.remove ) {
                                special.remove.call( elem, handleObj );
                            };
                        }

                        if ( pos != null ) {
                            break;
                        }
                    }
                }

                //如果某個事件的 事件列表刪除完了, 就把這個events【type】清空;
                // remove generic event handler if no more handlers exist
                if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
                    //mousein mouseout focusin fousout 對走這個;
                    if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
                        jQuery.removeEvent( elem, type, elemData.handle );
                    };

                    ret = null;
                    delete events[ type ];
                };
            };

            // Remove the expando if it's no longer used
            if ( jQuery.isEmptyObject( events ) ) {
                var handle = elemData.handle;
                if ( handle ) {
                    handle.elem = null;
                }

                delete elemData.events;
                delete elemData.handle;

                if ( typeof elemData === "function" ) {
                    jQuery.removeData( elem, eventKey, true );

                } else if ( jQuery.isEmptyObject( elemData ) ) {
                    jQuery.removeData( elem, undefined, true );
                }
            }
        },

        //trigger是給用戶用的;
        // bubbling is internal
        trigger: function( event, data, elem /*, bubbling */ ) {
            // Event object or event type
            var type = event.type || event,
                bubbling = arguments[3];

            //默認都是冒泡;
            if ( !bubbling ) {
                //自己新建一個event對象;
                event = typeof event === "object" ?
                    // jQuery.Event object
                    event[ jQuery.expando ] ? event :
                        // Object literal
                        jQuery.extend( jQuery.Event(type), event ) :
                    // Just the event type (string)
                    jQuery.Event(type);

                // 有!的代表觸發的是自定義的屬性更改事件, 對用戶來說,作用不多,有點像IE的onpropertychange;
                if ( type.indexOf("!") >= 0 ) {
                    event.type = type = type.slice(0, -1);
                    event.exclusive = true;
                };

                // Handle a global
                if ( !elem ) {
                    // 如果你要執行對應type全部事件,那么就要阻止默認事件
                    // 如果你不阻止冒泡的話會
                    // Don't bubble custom events when global (to avoid too much overhead)
                    // 這個event是假的,模擬出來的東東;
                    event.stopPropagation();

                    // Only trigger if we've ever bound an event for it
                    if ( jQuery.event.global[ type ] ) {
                        // XXX This code smells terrible. event.js should not be directly
                        // inspecting the data cache
                        jQuery.each( jQuery.cache, function() {
                            // internalKey variable is just used to make it easier to find
                            // and potentially change this stuff later; currently it just
                            // points to jQuery.expando
                            var internalKey = jQuery.expando,
                                internalCache = this[ internalKey ];
                            if ( internalCache && internalCache.events && internalCache.events[type] ) {
                                jQuery.event.trigger( event, data, internalCache.handle.elem );
                            }
                        });
                        //我不知道這里為什么不return掉;
                    }
                }

                // Handle triggering a single element

                // don't do events on text and comment nodes
                if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
                    return undefined;
                }

                // Clean up in case it is reused
                event.result = undefined;
                event.target = elem;

                // Clone the incoming data, if any
                data = jQuery.makeArray( data );
                data.unshift( event );
            }

            //
            event.currentTarget = elem;

            // Trigger the event, it is assumed that "handle" is a function
            var handle = elem.nodeType ?
                jQuery._data( elem, "handle" ) :
                (jQuery._data( elem, eventKey ) || {}).handle;

            //這個就是手動觸發事件了哇, data里面是有新建的event對象的;
            if ( handle ) {
                handle.apply( elem, data );
            };

            var parent = elem.parentNode || elem.ownerDocument;

            //手動觸發行內綁定的事件;
            // Trigger an inline bound script
            try {
                if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
                    if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
                        event.result = false;
                        event.preventDefault();
                    }
                }

                // prevent IE from throwing an error for some elements with some event types, see #3533
            } catch (inlineError) {}

            //我靠這個,又手動觸發了父級的對應事件,就是事件冒泡了 ,(jQ為什么考慮這么全面);
            if ( !event.isPropagationStopped() && parent ) {
                jQuery.event.trigger( event, data, parent, true );

                          //默認事件沒有被阻止的話;
            } else if ( !event.isDefaultPrevented() ) {
                var old,
                    target = event.target,
                    targetType = type.replace( rnamespaces, "" ),
                    isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
                    special = jQuery.event.special[ targetType ] || {};

                if ( (!special._default || special._default.call( elem, event ) === false) &&
                    !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {

                    try {
                        if ( target[ targetType ] ) {
                            // Make sure that we don't accidentally re-trigger the onFOO events
                            old = target[ "on" + targetType ];

                            if ( old ) {
                                target[ "on" + targetType ] = null;
                            }

                            jQuery.event.triggered = true;
                            target[ targetType ]();
                        }

                        // prevent IE from throwing an error for some elements with some event types, see #3533
                    } catch (triggerError) {}

                    if ( old ) {
                        target[ "on" + targetType ] = old;
                    }

                    jQuery.event.triggered = false;
                }
            }
        },

        //所綁定的事件, 這個方法的event是最初的事件對象;
        handle: function( event ) {
            var all, handlers, namespaces, namespace_re, events,
                namespace_sort = [],
                //除了event瀏覽器的調用這個事件以外, 用戶也可以模擬一個假的event,假的eventType等等,手動觸發哦;
                args = jQuery.makeArray( arguments );
                                                //對事件的event進行瀏覽器兼容統一處理
            event = args[0] = jQuery.event.fix( event || window.event );
            // currentTarget指的是綁定事件的元素;,
            // 如果是代理綁定的話, 那么事件函數里面的this不是綁定的元素, 用戶如果有需要的話通過currentTarget引用即可;
            event.currentTarget = this;

            // Namespaced event handlers
            //如果沒有命名空間的話就是true;
            /*比如 你通過
             $("body").bind("click.nameSpace0",function(){console.log(1)}) 綁定了事件,
             $("body").bind("click.nameSpace1",function(){console.log(1)})
                 當你左鍵點擊body元素的時候 這兩個綁定的事件都會觸發;
                 但是你想手動觸發nameSpace0這個事件的話,你可以直接trigger("click.nameSpace0");
                 事件的命名空間要看你怎么用了, 不用也沒大問題, 主要是解耦了各個事件函數;
              */
            all = event.type.indexOf(".") < 0 && !event.exclusive;

            //這個也只有用戶手動觸發的時候會走;
            if ( !all ) {
                namespaces = event.type.split(".");
                //事件名和事件命名空間拿出來;
                event.type = namespaces.shift();
                namespace_sort = namespaces.slice(0).sort();
                                          //開頭或者是一個dot;
                                                                                               //結尾或者是一個dot;
                namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
            };

            //別忘記了命名空間和liveHanler是不一樣的,別犯迷糊, 一個是事件的分類,一個是在父級上面綁定事件;
            event.namespace = event.namespace || namespace_sort.join(".");

            //獲取所有事件
                                         // eventKey === "events";
            events = jQuery._data(this, eventKey);

            //默認的events不會是function的, 這個是什么情況,好像是jQuery.special.type[ name ]那里面的事件;
            if ( typeof events === "function" ) {
                events = events.events;
            };

            //handler不是函數哦, 是所有有關這個事件的事件描述
            //標準的事件描述對象 應該是這樣的:{data : guid : handle :function(){}, name : "xx", type : "click"}
            //獲取對應的事件 比如 click 還是 mouseoout這樣的情況;
            handlers = (events || {})[ event.type ];

            //如果沒有綁定對應的事件就不走里面, 作用1:優化, 2:避免里面報錯;
            if ( events && handlers ) {
                // Clone the handlers to prevent manipulation
                //復制一個事件描述對象;
                handlers = handlers.slice(0);

                //迭代所有的事件;
                for ( var j = 0, l = handlers.length; j < l; j++ ) {
                    var handleObj = handlers[ j ];

                    // Filter the functions by class
                    if ( all || namespace_re.test( handleObj.namespace ) ) {
                        // Pass in a reference to the handler function itself
                        // So that we can later remove it
                        //事件
                        event.handler = handleObj.handler;
                        //事件的數據;為事件添加data這個這么重要嗎,還是我不會用;
                        event.data = handleObj.data;
                        //把handleObj事件描述對象掛到event事件對象上面
                        event.handleObj = handleObj;
                        //現在事件里面的事件對象就有了handleObj事件描述對象這東西了;
                        //執行事件;
                                                                  //默認的就一個event, 如果不是默認的就會把所有的參數重新傳進去;
                                                                  //利用這一點,我們可以把自己定義個發布者和訂閱者,而且參數自己填(event都是要的哇)
                        var ret = handleObj.handler.apply( this, args );

                        //進行而外的處理
                        if ( ret !== undefined ) {
                            //把數據保存到event.result, 下次執行的話,可以調用event.result獲取上次事件保存的值, 有用,HOW?
                            event.result = ret;
                            //對return false進行特殊的處理;
                            if ( ret === false ) {
                                event.preventDefault();
                                event.stopPropagation();
                            };
                        };

                        //不執行了哇, 跳出這個循環了;
                        if ( event.isImmediatePropagationStopped() ) {
                            break;
                        }
                    }
                }
            }

            return event.result;
        },

        props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),

        //復制一個事件對象, 統一事件對象的兼容;
        fix: function( event ) {
            //如果已經經過jQ處理過的事件對象;
            if ( event[ jQuery.expando ] ) {
                return event;
            }

            // store a copy of the original event object
            // and "clone" to set read-only properties
            var originalEvent = event;
            event = jQuery.Event( originalEvent );

            //根據原始的對象復制一個假的事件對象, 要復制的屬性分別是:
            //altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which;
            for ( var i = this.props.length, prop; i; ) {
                prop = this.props[ --i ];
                event[ prop ] = originalEvent[ prop ];
            };

            // Fix target property, if necessary
            if ( !event.target ) {
                //火狐下是srcElement ,chorme和ie都是target;
                // Fixes #1925 where srcElement might not be defined either
                event.target = event.srcElement || document;
            };

            // check if target is a textnode (safari)
            if ( event.target.nodeType === 3 ) {
                event.target = event.target.parentNode;
            }

            // Add relatedTarget, if necessary
            //修復IE下沒有relateTarget但是有fromeElement和toElement;
            if ( !event.relatedTarget && event.fromElement ) {
                event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
            };

            //這個也是IE的問題,沒有pageX和pageY;,根據clientX或者clientY加上界面滾動值在減去IE678下(Body,或者HTML標簽上)的2px問題;
            // Calculate pageX/Y if missing and clientX/Y available
            if ( event.pageX == null && event.clientX != null ) {
                var doc = document.documentElement,
                    body = document.body;

                event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
                event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
            };

            //DOM3規定使用whitch, 不用charCode也不用keyCode;
            // Add which for key events
            if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
                event.which = event.charCode != null ? event.charCode : event.keyCode;
            };

            //蘋果系統的META鍵就是window中的CTRL鍵;
            // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
            if ( !event.metaKey && event.ctrlKey ) {
                event.metaKey = event.ctrlKey;
            }
            //剛剛測試過了,筆記本上面的fn鍵即使按住了,事件對象 并沒有 按住fn鍵的屬性 顯示;
            // Add which for click: 1 === left; 2 === middle; 3 === right
            // Note: button is not normalized, so don't use it
            // 保證了當前不是通過鍵盤的事件;
                                 //保證了是button這個鍵存在值;
            if ( !event.which && event.button !== undefined ) {
                event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
            };

            return event;
        },

        // Deprecated, use jQuery.guid instead
        guid: 1E8,

        // Deprecated, use jQuery.proxy instead
        proxy: jQuery.proxy,

        //這幾個事件綁定的時候要特殊對待, 移除綁定也要特殊對待;
        special: {

            //當頁面加載完畢以后要初始化的幾個方法;
            ready: {
                // Make sure the ready event is setup
                setup: jQuery.bindReady,
                teardown: jQuery.noop
            },

            live: {
                //事件代理是根據事件描述handleObj對象 , 重新綁定事件, 不過handle是liveHandler,這個很重要;
                add: function( handleObj ) {
                    //這個就調用了add;
                    jQuery.event.add( this,
                        // click.^div^#div1^klass
                        liveConvert( handleObj.origType, handleObj.selector ),
                                                               //使用liveHandler作為事件對象;
                        jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
                },

                remove: function( handleObj ) {
                    jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
                }
            },

            beforeunload: {
                setup: function( data, namespaces, eventHandle ) {
                    // We only want to do this special case on windows
                    if ( jQuery.isWindow( this ) ) {
                        this.onbeforeunload = eventHandle;
                    }
                },

                teardown: function( namespaces, eventHandle ) {
                    if ( this.onbeforeunload === eventHandle ) {
                        this.onbeforeunload = null;
                    }
                }
            }
        }
    };

    //第一次運行就把正確的函數賦值給對象屬性;
    jQuery.removeEvent = document.removeEventListener ?
        function( elem, type, handle ) {
            if ( elem.removeEventListener ) {
                elem.removeEventListener( type, handle, false );
            }
        } :
        function( elem, type, handle ) {
            if ( elem.detachEvent ) {
                elem.detachEvent( "on" + type, handle );
            }
        };

    //事件對象的兼容;
    jQuery.Event = function( src ) {
        // Allow instantiation without the 'new' keyword
        // if !(this instanceof jQuery.Event) 也行;
        if ( !this.preventDefault ) {
            return new jQuery.Event( src );
        }

        // Event object
        // 一般來說src是對象的話,應該是系統提供的事件對象;
        if ( src && src.type ) {
            this.originalEvent = src;
            this.type = src.type;

            // Events bubbling up the document may have been marked as prevented
            // by a handler lower down the tree; reflect the correct value.
            this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
                src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;

            // Event type
        } else {
            this.type = src;
        }

        // timeStamp is buggy for some events on Firefox(#3843)
        // So we won't rely on the native value
        this.timeStamp = jQuery.now();

        // Mark it as fixed
        this[ jQuery.expando ] = true;
    };

    function returnFalse() {
        return false;
    }
    function returnTrue() {
        return true;
    }

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
    jQuery.Event.prototype = {
        preventDefault: function() {
            this.isDefaultPrevented = returnTrue;

            var e = this.originalEvent;
            if ( !e ) {
                return;
            }

            // if preventDefault exists run it on the original event
            if ( e.preventDefault ) {
                e.preventDefault();

                // otherwise set the returnValue property of the original event to false (IE)
            } else {
                e.returnValue = false;
            }
        },
        stopPropagation: function() {
            this.isPropagationStopped = returnTrue;

            var e = this.originalEvent;
            if ( !e ) {
                return;
            }
            // if stopPropagation exists run it on the original event
            if ( e.stopPropagation ) {
                e.stopPropagation();
            }
            // otherwise set the cancelBubble property of the original event to true (IE)
            e.cancelBubble = true;
        },
        stopImmediatePropagation: function() {
            this.isImmediatePropagationStopped = returnTrue;
            this.stopPropagation();
        },
        isDefaultPrevented: returnFalse,
        isPropagationStopped: returnFalse,
        isImmediatePropagationStopped: returnFalse
    };

    //模擬mouseenter和mouseleave;
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
    var withinElement = function( event ) {
            // Check if mouse(over|out) are still within the same parent element
            var parent = event.relatedTarget;

            // FiwithinElementrefox sometimes assigns relatedTarget a XUL element
            // which we cannot access the parentNode property of
            try {
                // Traverse up the tree
                while ( parent && parent !== this ) {
                    parent = parent.parentNode;
                };

                if ( parent !== this ) {
                    // set the correct event type
                    event.type = event.data;

                    // handle event if we actually just moused on to a non sub-element
                    jQuery.event.handle.apply( this, arguments );
                }

                // assuming we've left the element since we most likely mousedover a xul element
            } catch(e) { }
        },

// In case of event delegation, we only need to rename the event.type,
// liveHandler will take care of the rest.
        delegate = function( event ) {
            event.type = event.data;
            jQuery.event.handle.apply( this, arguments );
        };

// Create mouseenter and mouseleave events
    jQuery.each({
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    }, function( orig, fix ) {
        jQuery.event.special[ orig ] = {
            //setup就是綁定事件
            setup: function( data ) {
                jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
            },
            //teardown就是取消事件
            teardown: function( data ) {
                jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
            }
        };
    });

// submit delegation
    if ( !jQuery.support.submitBubbles ) {

        jQuery.event.special.submit = {
            //綁定事件
            setup: function( data, namespaces ) {
                if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
                    jQuery.event.add(this, "click.specialSubmit", function( e ) {
                        var elem = e.target,
                            type = elem.type;

                        if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
                            e.liveFired = undefined;
                            return trigger( "submit", this, arguments );
                        }
                    });

                    jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
                        var elem = e.target,
                            type = elem.type;

                        if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
                            e.liveFired = undefined;
                            return trigger( "submit", this, arguments );
                        }
                    });

                } else {
                    return false;
                }
            },

            //取消事件;
            teardown: function( namespaces ) {
                jQuery.event.remove( this, ".specialSubmit" );
            }
        };

    }
// setup和teardown這些東西自己添加擴展事件, 就是閉包閉包又是閉包,一層一層一層又是一層;
// change delegation, happens here so we have bind.
    if ( !jQuery.support.changeBubbles ) {

        var changeFilters,

            getVal = function( elem ) {
                var type = elem.type, val = elem.value;

                if ( type === "radio" || type === "checkbox" ) {
                    val = elem.checked;

                } else if ( type === "select-multiple" ) {
                    val = elem.selectedIndex > -1 ?
                        jQuery.map( elem.options, function( elem ) {
                            return elem.selected;
                        }).join("-") :
                        "";

                } else if ( elem.nodeName.toLowerCase() === "select" ) {
                    val = elem.selectedIndex;
                }

                return val;
            },

            testChange = function testChange( e ) {
                var elem = e.target, data, val;

                if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
                    return;
                }

                data = jQuery._data( elem, "_change_data" );
                val = getVal(elem);

                // the current data will be also retrieved by beforeactivate
                if ( e.type !== "focusout" || elem.type !== "radio" ) {
                    jQuery._data( elem, "_change_data", val );
                }

                if ( data === undefined || val === data ) {
                    return;
                }

                if ( data != null || val ) {
                    e.type = "change";
                    e.liveFired = undefined;
                    return jQuery.event.trigger( e, arguments[1], elem );
                }
            };

        jQuery.event.special.change = {
            filters: {
                focusout: testChange,

                beforedeactivate: testChange,

                click: function( e ) {
                    var elem = e.target, type = elem.type;

                    if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
                        return testChange.call( this, e );
                    }
                },

                // Change has to be called before submit
                // Keydown will be called before keypress, which is used in submit-event delegation
                keydown: function( e ) {
                    var elem = e.target, type = elem.type;

                    if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
                        (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
                        type === "select-multiple" ) {
                        return testChange.call( this, e );
                    }
                },

                // Beforeactivate happens also before the previous element is blurred
                // with this event you can't trigger a change event, but you can store
                // information
                beforeactivate: function( e ) {
                    var elem = e.target;
                    jQuery._data( elem, "_change_data", getVal(elem) );
                }
            },

            //綁定事件
            setup: function( data, namespaces ) {
                if ( this.type === "file" ) {
                    return false;
                }

                for ( var type in changeFilters ) {
                    jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                }

                return rformElems.test( this.nodeName );
            },

            //取消事件
            teardown: function( namespaces ) {
                jQuery.event.remove( this, ".specialChange" );

                return rformElems.test( this.nodeName );
            }
        };

        changeFilters = jQuery.event.special.change.filters;

        // Handle when the input is .focus()'d
        changeFilters.focus = changeFilters.beforeactivate;
    }

    function trigger( type, elem, args ) {
        args[0].type = type;
        return jQuery.event.handle.apply( elem, args );
    }

    //修復瀏覽器fousein和fouseout支持;
// Create "bubbling" focus and blur events
    if ( document.addEventListener ) {
        jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
            jQuery.event.special[ fix ] = {
                setup: function() {
                    this.addEventListener( orig, handler, true );
                },
                teardown: function() {
                    this.removeEventListener( orig, handler, true );
                }
            };

            function handler( e ) {
                e = jQuery.event.fix( e );
                e.type = fix;
                return jQuery.event.handle.call( this, e );
            }
        });
    }

    //這個是繼承到實例原型上面的代碼;
    //利用閉包實現了bind和 one;  減少代碼量;
    jQuery.each(["bind", "one"], function( i, name ) {
        jQuery.fn[ name ] = function( type, data, fn ) {
            // Handle object literals
            //處理傳進來的是對象的情況, 調用對應的方法; 一接口的多種實用方法;
            if ( typeof type === "object" ) {
                for ( var key in type ) {
                    this[ name ](key, data, type[key], fn);
                };
                return this;
            };

            //修正參數 data , fn;
                                              //data為什么要===false $("div").bind("click",false,function() {});怎么辦哇;
            if ( jQuery.isFunction( data ) || data === false ) {
                fn = data;
                data = undefined;
            };

            //初始化綁定的函數
            //如果是one的話就是重新定義事件函數, 這個事件函數也是一個閉包, 引用了fn, 需要強調的是 $.proxy的作用是設置匿名事件函數的guid和fn一樣;;
            var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
                jQuery( this ).unbind( event, handler );
                return fn.apply( this, arguments );
            }) : fn;

            //對unload的事件進行優化, 本身unload的事件就是不能一直掛在元素上面的;
            if ( type === "unload" && name !== "one" ) {
                this.one( type, data, fn );
            } else {
                for ( var i = 0, l = this.length; i < l; i++ ) {
                    //調用工具,  現在的參數一定是對的,  里面就不用擔心用戶亂傳參數進來了;
                    jQuery.event.add( this[i], type, handler, data );
                }
            }

            return this;
        };
    });

    //
    jQuery.fn.extend({
        unbind: function( type, fn ) {
            // Handle object literals
            // 這個和unbind做一樣的處理;
            if ( typeof type === "object" && !type.preventDefault ) {
                for ( var key in type ) {
                    this.unbind(key, type[key]);
                };

            } else {
                for ( var i = 0, l = this.length; i < l; i++ ) {
                    //unbind;
                    jQuery.event.remove( this[i], type, fn );
                }
            }

            return this;
        },

        //delegate也是調用live哇;
        delegate: function( selector, types, data, fn ) {
            return this.live( types, data, fn, selector );
        },

        //unbind是調用die,1.6以后的版本好像沒有live和die了;
        undelegate: function( selector, types, fn ) {
            if ( arguments.length === 0 ) {
                return this.unbind( "live" );
            } else {
                return this.die( types, null, fn, selector );
            }
        },

        trigger: function( type, data ) {
            return this.each(function() {
                jQuery.event.trigger( type, data, this );
            });
        },

        //trigger 和triggerHandler的區別是 后者 觸發了當前的第一個元素的對應事件, 而且阻止了默認操作和冒泡;
        triggerHandler: function( type, data ) {
            if ( this[0] ) {
                var event = jQuery.Event( type );
                event.preventDefault();
                event.stopPropagation();
                jQuery.event.trigger( event, data, this[0] );
                //事件對象有一個result, 說明 迭代執行事件的時候的返回值被保存到了event.result去;
                return event.result;
            };
        },

        //toggle和hover就是對click進行了封裝而已;
        toggle: function( fn ) {
            // Save reference to arguments for access in closure
            var args = arguments,
                i = 1;

            // link all the functions, so any of them can unbind this click handler
            // 把這幾個事件函數的guid設置成一樣的數字,保證了使用unbind的時候可以取消這個click事件;
            // i從第一個開始迭代到最后一個;
            while ( i < args.length ) {
                jQuery.proxy( fn, args[ i++ ] );
            };
            /*
            這個循環和for( var i=0; i<len ;i++); for( var i=0; i<len ;) {i++}這是一樣的;
             while ( i < args.length ) {
                jQuery.proxy( fn, args[ i ] );
                i++;
             };
             */
            //又用了一個閉包, 綁定這個了fn這個時間;
            return this.click( jQuery.proxy( fn, function( event ) {
                // Figure out which function to execute
                // i現在是總數;
                /*
                 0%4 ==>>  0
                 1%4 ==>>  1
                 2%4 ==>>  2
                 3%4 ==>>  3
                 4%4 ==>>  0

                 //內部用的_data
                 jQuery._data = function ( elem, name, data ) {
                    return jQuery.data( elem, name, data, true );
                 };
                 */
                var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
                jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );

                // Make sure that clicks stop
                //為什么要阻止默認事件哇;
                event.preventDefault();

                //執行
                // and execute the function
                return args[ lastToggle ].apply( this, arguments ) || false;
            }));
        },

        hover: function( fnOver, fnOut ) {
            return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
        }
    });

    var liveMap = {
        focus: "focusin",
        blur: "focusout",
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    };

    //兩個閉包, 減少代碼量;               //這里面的i沒有什么用, name才有用;
    jQuery.each(["live", "die"], function( i, name ) {
        jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
            var type, i = 0, match, namespaces, preType,
                //事件代理的text; 沒有的話傳進來默認為當前的元素的選擇符;
                selector = origSelector || this.selector,
            //如果有origSelector就是 當前的元素, 否則 document?, ,為context綁定事件;
                context = origSelector ? this : jQuery( this.context );

            //和bind unbind一樣,提供object的方式傳參; write less do more;
            if ( typeof types === "object" && !types.preventDefault ) {
                for ( var key in types ) {
                    context[ name ]( key, data, types[key], selector );
                };
                return this;
            };

            //處理參數, 實在不懂data有毛用哇;
            //$("body").live("click",function(){},"div");
            //$("body").live("click","",function(){},"div");
            if ( jQuery.isFunction( data ) ) {
                fn = data;
                data = undefined;
            };

            //支持多事件的情況; $("body").live("click mousein mouseout","",function(){},"div");
            types = (types || "").split(" ");

            while ( (type = types[ i++ ]) != null ) {
                // rnamespace = /\.(.*)$/;
                // 事件的命名空間, 如果你綁定了click事件,而且要區分click事件的類別分別不同情況觸發,就可以使用命名空間;
                match = rnamespaces.exec( type );
                namespaces = "";

                if ( match )  {
                    /*
                    /dfd/.exec("eadfdsdfe.sdfsdfe");
                    ["dfd"]
                    */
                    namespaces = match[0]; //命名空間
                    type = type.replace( rnamespaces, "" ); //類型
                };

                //系統沒有hover事件,把hover事件替換成mouseenter和mouseleave;
                if ( type === "hover" ) {
                    types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
                    continue;
                };

                preType = type;

                //為了讓事件冒泡吧,所以做了處理;
                /*
                     var liveMap = {
                         focus: "focusin",
                         blur: "focusout",
                         mouseenter: "mouseover",
                         mouseleave: "mouseout"
                     };
                 */
                if ( type === "focus" || type === "blur" ) {
                    types.push( liveMap[ type ] + namespaces );
                    type = type + namespaces;
                } else {
                    //這個不靠譜吧,mouseenter和mouseleave 就chrome的低版本不支持啊, 為什么要全部使用mouseover和mouseout進行模擬呢;
                    type = (liveMap[ type ] || type) + namespaces;
                };

                //現在還在閉包內部,所以要根據情況判斷是添加事件還是移除事件;
                if ( name === "live" ) {
                    // bind live handler
                    //context = origSelector ? this : jQuery( this.context );別忘記了context是this的引用或者其他對象的引用;
                    for ( var j = 0, l = context.length; j < l; j++ ) {
                                        //要綁定的對象
                                                              //liveConvert("click",".class0 .wa")  ==>>  "click.`class0&`wa"
                                                              //liveConvert("click",".class0 .wa #div")  ==>>  "click.`class0&`wa&#div"
                                                              //內部自己約定了事件代理的描述;
                        jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
                             //這個是事件的描述, 高版本的jQ把事件代理的描述和事件描述合并在一起了;
                                                                                                                     //preType指的是未對用戶傳進來事件名字進行處理的事件名字;
                            { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
                    };
                } else {
                    // unbind live handler
                    //這個直接使用unbind 這樣好嗎 john....;
                    context.unbind( "live." + liveConvert( type, selector ), fn );
                };
            };
            return this;
        };
        //live的總結 : 事件的代理是在live處理的,使用了live綁定的元素綁定的事件默認是live開頭, 后面就是懂得自然懂了。 比如:live."click.`class0&`wa&#div"
    });

    //liveHandler也是挺簡單, 主流程是根據事件對象的 事件描述對象 匹配出符合命名空間的綁定函數, 然后讓他執行;
    function liveHandler( event ) {
        var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
            elems = [],
            selectors = [],
            events = jQuery._data( this, eventKey );

        if ( typeof events === "function" ) {
            events = events.events;
        }

        // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
            return;
        }

        //匹配合適的命名空間哇;
        if ( event.namespace ) {
            namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
        }

        event.liveFired = this;

        //復制一個事件描述對象的數組;
        var live = events.live.slice(0);

        for ( j = 0; j < live.length; j++ ) {
            handleObj = live[j];

            //命名空間符合event.type 就把這個函數保存起來;
            if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
                selectors.push( handleObj.selector );

            } else {
                live.splice( j--, 1 );
            }
        }

        //這個返回的是所有匹配的元素;
        match = jQuery( event.target ).closest( selectors, event.currentTarget );

        //這個是雙重循環,過濾合適的element
        for ( i = 0, l = match.length; i < l; i++ ) {
            close = match[i];

            for ( j = 0; j < live.length; j++ ) {
                handleObj = live[j];

                if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
                    elem = close.elem;
                    related = null;

                    // Those two events require additional checking
                    if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
                        event.type = handleObj.preType;
                        related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
                    }

                    //把元素和事件對象保存起來;
                    if ( !related || related !== elem ) {
                        elems.push({ elem: elem, handleObj: handleObj, level: close.level });
                    }
                }
            }
        }

        //一個一個執行, event下的result屬性依然保存著上一個事件的返回值;
        for ( i = 0, l = elems.length; i < l; i++ ) {
            match = elems[i];

            if ( maxLevel && match.level > maxLevel ) {
                break;
            }

            event.currentTarget = match.elem;
            event.data = match.handleObj.data;
            event.handleObj = match.handleObj;

            ret = match.handleObj.origHandler.apply( match.elem, arguments );

            //這個和handle里面的代碼重復了, jQ高版本做了優化;
            if ( ret === false || event.isPropagationStopped() ) {
                maxLevel = match.level;

                if ( ret === false ) {
                    stop = false;
                }
                if ( event.isImmediatePropagationStopped() ) {
                    break;
                }
            }
        }

        return stop;
    };

    //提供給事件代理用的;
    function liveConvert( type, selector ) {
        return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
    };

    //shortcut, 提供實例方法上面快捷方式調用
    jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
        "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
        "change select submit keydown keypress keyup error").split(" "), function( i, name ) {

        // Handle event binding
        jQuery.fn[ name ] = function( data, fn ) {
            if ( fn == null ) {
                fn = data;
                data = null;
            }

            return arguments.length > 0 ?
                this.bind( name, data, fn ) :
                this.trigger( name );
        };

        if ( jQuery.attrFn ) {
            jQuery.attrFn[ name ] = true;
        }
    });

   快3點了,我*,睡了。。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()