jQ1.5中的事件系统(低版本的事件系统)

  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点了,我*,睡了。。

posted @ 2015-01-03 02:37  方方和圆圆  阅读(891)  评论(0编辑  收藏  举报

再过一百年, 我会在哪里?