jQuery源碼分析--Event模塊(3)

  最後剩下了事件的手動觸發了。jQuery提供了兩個函數trigger和triggerHandler來手動觸發事件,能夠觸發原生事件和自定義的事件。這個觸發不單隻會觸發有jQuery綁定事件,並且也會觸發原生的行內綁定事件。trigger和triggerHander的區別是:
  trgger:會對匹配的全部元素都調用jQuery.event.trrger,並且會冒泡,會觸發瀏覽器默認行爲。返回值爲jQuery對象
  triggerHandler:只會對第一個匹配的元素調用jQuery.event.trrger,並且不會冒泡,不會觸發瀏覽器默認行爲。爲函數的返回值
  能夠看到都是經過底層的jQuery.event.trrger工具方法來實現。那jQuery.event.trrger是如何工做的呢。node

  1. 構造從當前節點到window的一個冒泡路徑,用數組來存儲。
  2. 遍歷這個路徑數組,在沒有被阻止冒泡的狀況下,調用每一個節點的jQuery的主監聽函數 和 元素的行內監聽函數。
  3. 最後用原生的事件觸發函數(click,focus)來觸發默認行爲。並設定一個jQuery.event.triggered標誌來標誌是觸發默認行爲,避免再次觸發事件。
    最後是源代碼。
    trigger: function( event, data, elem, onlyHandlers ) {//event 能夠是事件類型、自定義事件對象或jquery事件對象
    
            var i, cur, tmp, bubbleType, ontype, handle, special,
                eventPath = [ elem || document ],//冒泡路勁
                type = hasOwn.call( event, "type" ) ? event.type : event,//判斷event是對象(自定義和jquery)仍是類型字符串
                namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];//命名空間
    
            cur = tmp = elem = elem || document;//cur指向當前元素的祖先元素
    
            // Don't do events on text and comment nodes
            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {//排除文本節點和註釋節點
                return;
            }
    
            // focus/blur morphs to focusin/out; ensure we're not firing them right now    rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
            if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {//過濾掉foces/blur事件的默認行爲,後面統一用focusin/focusout
                return;
            }
    
            if ( type.indexOf(".") >= 0 ) {//解析事件類型和命名空間並對命名空間排序 
                // Namespaced trigger; create a regexp to match event type in handle()
                namespaces = type.split(".");
                type = namespaces.shift();
                namespaces.sort();
            }
            ontype = type.indexOf(":") < 0 && "on" + type;//有:號時不調用行內監聽事件
    
            // Caller can pass in a jQuery.Event object, Object, or just an event type string
            event = event[ jQuery.expando ] ?//判斷是否是jquery監聽對象,不是建立。
                event :
                new jQuery.Event( type, typeof event === "object" && event );
    
            // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
            event.isTrigger = onlyHandlers ? 2 : 3;
            event.namespace = namespaces.join(".");
            event.namespace_re = event.namespace ?
                new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
                null;
    
            // Clean up the event in case it is being reused
            event.result = undefined;//最後一個有返回值的函數的返回值
            if ( !event.target ) {//修正target
                event.target = elem;
            }
    
            // Clone any incoming data and prepend the event, creating the handler arg list
            data = data == null ?//將附加數據(若是有)和事件對象封裝成數組。方便apply調用
                [ event ] :
                jQuery.makeArray( data, [ event ] );
    
            // Allow special events to draw outside the lines
            special = jQuery.event.special[ type ] || {};
            if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {//先調用修正對象的觸發函數來觸發
                return;
            }
    
            // Determine event propagation path in advance, per W3C events spec (#9951)   構造冒泡路勁
            // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
            if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {//排除onlyHandlers=true、load、已經到window對象了
    
                bubbleType = special.delegateType || type;//優先使用修正對象的屬性
                if ( !rfocusMorph.test( bubbleType + type ) ) {//初始化cur爲當前元素的父元素(排除focus/blur)
                    cur = cur.parentNode;
                }
                for ( ; cur; cur = cur.parentNode ) {//遍歷
                    eventPath.push( cur );
                    tmp = cur;//tmp指向所能到達的最頂層元素
                }
    
                // Only add window if we got to document (e.g., not plain obj or detached DOM)
                if ( tmp === (elem.ownerDocument || document) ) {//若是最頂層是document再添加window
                    eventPath.push( tmp.defaultView || tmp.parentWindow || window );
                }
            }
    
            // Fire handlers on the event path
            i = 0;
            while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {//遍歷冒泡路勁  觸發監聽函數
    
                event.type = i > 1 ?
                    bubbleType :
                    special.bindType || type;
    
                // jQuery handler
                handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );//取出主監聽函數
                if ( handle ) {//調用主監聽函數
                    handle.apply( cur, data );
                }
    
                // Native handler
                handle = ontype && cur[ ontype ];
                if ( handle && handle.apply && jQuery.acceptData( cur ) ) {//執行行內事件監聽函數
                    event.result = handle.apply( cur, data );
                    if ( event.result === false ) {//返回值爲false表明阻止默認行爲
                        event.preventDefault();
                    }
                }
            }
            event.type = type;//回覆type
    
            // If nobody prevented the default action, do it now
            if ( !onlyHandlers && !event.isDefaultPrevented() ) {//沒有阻止默認行爲
    
                if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
                    jQuery.acceptData( elem ) ) {
    
                    // Call a native DOM method on the target with the same name name as the event.
                    // Don't do default actions on window, that's where global variables be (#6170)
                    if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
    
                        // Don't re-trigger an onFOO event when we call its FOO() method  避免再次觸發行內監聽函數
                        tmp = elem[ ontype ];
    
                        if ( tmp ) {
                            elem[ ontype ] = null;
                        }
    
                        // Prevent re-triggering of the same event, since we already bubbled it above   避免再次觸發主函數
                        jQuery.event.triggered = type;
                        elem[ type ]();
                        jQuery.event.triggered = undefined;
    
                        if ( tmp ) {//恢復
                            elem[ ontype ] = tmp;
                        }
                    }
                }
            }
    
            return event.result;
        },
相關文章
相關標籤/搜索