jquery的show/hide/toggle詳解


通過閱讀源碼我們發現show,hide,toggle調用了showHide和isHidden這2個方法,所以我們要搞明白原理必須先看一下這2個方法。

jQuery.fn.extend({
    
        .................

    show: function() {
        return showHide( this, true );
    },
    hide: function() {
        return showHide( this );
    },
    toggle: function( state, fn2 ) {
        var bool = typeof state === "boolean";

        if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {
            return eventsToggle.apply( this, arguments );
        }

        return this.each(function() {
            if ( bool ? state : isHidden( this ) ) {
                jQuery( this ).show();
            } else {
                jQuery( this ).hide();
            }
        });
    }
});

isHidden比較簡單,接受2個參數,調用了jq的工具方法css來判斷當前的display是否為none,為none返回真,否則走后面的contains方法
contains用於判斷元素是否包含在元素所在的文檔中,elem.ownerDocument其實就是document, elem是元素
由此可以看出,如果元素不包含在當前的文檔中,jq也認為這個元素是隱藏的,比如document.createElement創建出來的元素。

function isHidden( elem, el ) {
    elem = el || elem;
    return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
}

showHide方法代碼有點長,我們發現jQuery._data(),css_defaultDisplay(),curCSS(),這3個東西又是什么呢,稍后再分析。
showHide接受2個參數,elements : 元素集合 、 show : 布爾值(true表示顯示 、false表示隱藏)

function showHide( elements, show ) {
    var elem, display,
        values = [],
        index = 0,
        length = elements.length;

    for ( ; index < length; index++ ) {
        elem = elements[ index ];
        if ( !elem.style ) {
            continue;
        }
        values[ index ] = jQuery._data( elem, "olddisplay" );
        if ( show ) {
            // Reset the inline display of this element to learn if it is
            // being hidden by cascaded rules or not
            if ( !values[ index ] && elem.style.display === "none" ) {
                elem.style.display = "";
            }

            // Set elements which have been overridden with display: none
            // in a stylesheet to whatever the default browser style is
            // for such an element
            if ( elem.style.display === "" && isHidden( elem ) ) {
                values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
            }
        } else {
            display = curCSS( elem, "display" );

            if ( !values[ index ] && display !== "none" ) {
                jQuery._data( elem, "olddisplay", display );
            }
        }
    }

    // Set the display of most of the elements in a second loop
    // to avoid the constant reflow
    for ( index = 0; index < length; index++ ) {
        elem = elements[ index ];
        if ( !elem.style ) {
            continue;
        }
        if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
            elem.style.display = show ? values[ index ] || "" : "none";
        }
    }

    return elements;
}

我們先看代碼最下面,它遍歷了元素集合,如果元素沒有樣式則跳過,哪些元素沒有樣式呢,比如文本節點、注釋節點等等。。。
我們特別注意一下這句代碼:elem.style.display = show ? values[ index ] || "" : "none";
當為show時,它並沒有直接寫"block"而是一個變量賦值的操作,當它沒有時才會空。
這里主要是為了區分元素的樣式屬性,比如span是行內元素如果給他賦blcok就變成塊元素了,所以values[ index ]這個變量是在上面的代碼處理過得到的。

for ( index = 0; index < length; index++ ) {
    elem = elements[ index ];
    if ( !elem.style ) {
        continue;
    }
    if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
        elem.style.display = show ? values[ index ] || "" : "none";
    }
}

現在回過頭看代碼的開始部分就容易理解了,先獲取元素默認的display值,一般第一次獲取肯定是空的,因為這個值需要在隱藏時保存,我們看到下面就明白了,這里先不解釋。

values[ index ] = jQuery._data( elem, "olddisplay" );

接着往下看,if(show)里面做了2個判斷,第一個判斷沒什么說的,重點看第二個。
當行內樣式為空,且是一個隱藏的元素(css樣式表中display:none),則執行了一個比較有意思的操作。
利用css_defaultDisplay方法能夠正確的獲取到元素的樣式屬性,什么意思呢?
我們可以這樣想,當一個元素初始的時候給一個display為none,我們肯定獲取不到該元素顯示時所對應的值,也就是說我們不知道是block,或是inline,換句話說,我們不知道該元素是塊元素還是行內元素。
可是css_defaultDisplay方法能夠做到,那么它是怎么實現的呢?

這里就不貼css_defaultDisplay的源碼了,簡單說一下它的原理,其實很簡單
首先獲取元素的nodeName(標簽名) -> createElement動態的創建 -> 添加到document.body中 -> 獲取該元素的display(元素默認的display都是顯示,如div是block,span
是inline) -> 刪除該元素

if ( show ) {
        if ( !values[ index ] && elem.style.display === "none" ) {
            elem.style.display = "";
        }

        if ( elem.style.display === "" && isHidden( elem ) ) {
            values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
        }
    } else {
        ......................
    }

好了,if(show)里面的代碼分析完了,我們再看分析一下else里面的代碼
先獲取display的值,再是一個判斷,如果values[ index ]沒有值並且display不是none,則把元素當前的display值保存起來,方便在if(show)中使用,就是上面提到的,不記得的話再去回顧一下。

if ( show ) {
        .....................
} else {
    display = curCSS( elem, "display" );

    if ( !values[ index ] && display !== "none" ) {
        jQuery._data( elem, "olddisplay", display );
    }
}

 

最后分析一下toggle方法,其實也蠻簡單的,遍歷元素集合,判斷bool是否為一個布爾值,是的話則判斷state,state為真則調show方法,為假則調hide方法
如果bool不是布爾值,則判斷該元素是否隱藏,隱藏的話則調show方法,顯示的話則調hide方法

toggle: function( state, fn2 ) {
    var bool = typeof state === "boolean";

    ...........................

    return this.each(function() {
        if ( bool ? state : isHidden( this ) ) {
            jQuery( this ).show();
        } else {
            jQuery( this ).hide();
        }
    });
}

總結:

這3個方法最難的是show方法,先要獲取values[ index ]保存的display默認值,如果它是空的,則需要調用css_defaultDisplay方法來變向的獲取。
hide方法只是簡單的dispaly=none,toggle只是根據state或者isHidden來調show方法或hide方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM