1. 程式人生 > >jQuery2.0.3原始碼分析系列之(29) 視窗尺寸

jQuery2.0.3原始碼分析系列之(29) 視窗尺寸

  • .width()

    基礎回顧

    一般的,在獲取瀏覽器視窗的大小和位置時,有以下幾個屬性可以使用:

    在不同的瀏覽器中,以下12個屬性所代表的意義也是不一樣的

    特別需要注意的是,當使用或者不使用<!DOCTYPE>宣告顯示一個文件的時候,以上12個屬性的意義也會發生變化。

    特在IE 9中,無論是否使用<!DOCTYPE>宣告顯示一個文件,document.documentElement和document.body中的相關屬性的意義總是相同的。這點與IE 6/7/8表現不一樣。

    正常情況:Firefox/Chrome/Safari(帶<!DOCTYPE>宣告)

    視窗顯示區(可視區域)的寬度和高度,包括滾動條區域

    window.innerHeight
    window.innerWidth

    視窗顯示區(可視區域)的寬度和高度,包括滾動條區域

    document.documentElement.clientHeight
    document.documentElement.clientWidth

    <body>元素的寬度和高度(注意,包括了不可見的區域)

    document.body.clientHeight
    document.body.clientWidth

    當前頁面相對於視窗顯示區左上角的 X /Y位置,即水平/垂直滾動條已滾動的距離

    window.pageXOffset
    window.pageYOffset

    Firefox:當前頁面相對於視窗顯示區左上角的 X /Y位置,即水平/垂直滾動條已滾動的距離,同window.pageXOffset/pageYOffset

    Chrome/Safari:總為0

    document.documentElement.scrollLeft
    document.documentElement.scrollTop

    FireFox:總為0

    Chrome/Safari:當前頁面相對於視窗顯示區左上角的 X /Y位置,即水平/垂直滾動條已滾動的距離,同window.pageXOffset/pageYOffse

    document.body.scrollLeft
    document.body.scrollTop

    如下一圖說明所有問題

    首先先解釋下普通元素和非普通元素,

    非普通元素是指window,document這些 元素物件,

    普通元素是指除window,document之外的元素,如:div

    innerHeight屬性:視窗中文件顯示區域的高度,不包括選單欄、工具欄等部分。該屬性可讀可寫。

    IE不支援該屬性,IE中body元素的clientHeight屬性與該屬性相同。

    innerWidth屬性:視窗中文件顯示區域的寬度,同樣不包括邊框。該屬性可讀可寫。

    IE不支援該屬性,IE中body元素的clientWidth屬性與該屬性相同。

           clientHeight與clientWidth屬性是隻讀的。

           另外,IE不支援outerWidth、outerHeight屬性。

    相容IE與DOM瀏覽器,如何獲取視窗中文件顯示區域的寬度及高度,使用?:條件語句,如下:

    windows.innerWidth ? windows.innerWidth : document.body.clientWidth;

    windows.innerHeight ? windows.innerHeight : document.body.clientHeight

    原始碼實現

    window,document

    $(window).height()     代表了當前可見區域的大小

    $(document).height()  則代表了整個文件的高度

    注意當瀏覽器視窗大小改變時(如最大化或拉大視窗後) (window).height()隨之改變,但是(window).height()隨之改變,但是(document).height()是不變的。

    window 反映的是檢視視窗,沒有用window.innerWidth

    原始碼是通過document.documentElement

    return elem.document.documentElement[ "client" + name ];

    因為有些樣式不是簡單的讀寫屬性就可以的,比如width就不是簡單地讀取el.style.width。為了解決這個問題,jquery定義了一個屬性 $.cssHooks,這裡可以自定義對某個屬性的get和set操作。而且jquery中就是用cssHooks來處理某些特殊屬性

    具體涉及了:

    1. borderWidth: Object
    2. height: Object
    3. margin: Object
    4. opacity: Object
    5. padding: Object
    6. width: Object
    做dom選擇器的時候,需要考慮各種相容問題,所以就算是定位2X的原始碼,只考慮標準的w3c,不免也要額外才處理一些屬性
    比如盒子模型的解釋:

    說到 IE 的 bug,它對於“盒模型”的錯誤解釋:在 IE5.x 以及 Quirks 模式的 IE6/7 中,將 border 與 padding 都包含在 width 之內。這為前端工程師的工作平添了不少麻煩,幾戶每個需要定義尺寸的 box 都要思量一下:是否觸發了“盒模型 bug”?

    同時,由於另一撮瀏覽器對標準的遵從,我們在精確定義一個在有限空間內顯示的 box 時,也需要計算一下:留給它的空間只有那麼大,刨去 border 和 padding,我們該把它的 width 寫成多少呢?

    這種情況在 CSS3 時代有了改善,得益於這個叫做 box-sizing 的屬性,它具有“content-box”和“border-box”兩個值。

    定義 box-sizing: content-box; 時,瀏覽器對盒模型的解釋遵從我們之前認識到的 W3C 標準;

    定義 box-sizing: border-box; 時,瀏覽器對盒模型的解釋與 IE6 相同;

    我們看看width,height方法

    jQuery還提供兩個單獨的API,width, height獲取元素的長寬,可以取得隱藏元素的長寬

    這裡是屬於cssHooks處理

    複製程式碼

    jQuery.each([ "height", "width" ], function( i, name ) {
            jQuery.cssHooks[ name ] = {
                get: function( elem, computed, extra ) {
                    if ( computed ) {
                        return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
                            jQuery.swap( elem, cssShow, function() {
                                return getWidthOrHeight( elem, name, extra );
                            }) :
                            getWidthOrHeight( elem, name, extra );
                    }
                },
                set: function( elem, value, extra ) {
                    var styles = extra && getStyles( elem );
                    return setPositiveNumber( elem, value, extra ?
                        augmentWidthOrHeight(
                            elem,
                            name,
                            extra,
                            jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
                            styles
                        ) : 0
                    );
                }
            };
        });

    複製程式碼

    出於效能的考量,瀏覽器對隱藏元素的樣式是統統不計算的,有一套預設值返回給你,對於長寬就是0。對於動畫來說,這有點不方便,比如show, slideDown等特效!因此jQuery等偷偷讓它們顯示出來,取得精確值,再隱藏回來,這個是由jquery.swap方法來處理

    所以最終通過getWidthOrHeight方法獲取了width,height

    當然正常情況下offsetWidth,offsetHeigth是可以了,但是某些元素比如SVG,MathML返回尺寸出錯(這裡不考慮)

    即便如此CSS3還增加了一個box-sizing選擇盒子模型,於是有了augmentWidthOrHeight這個方法。

    augmentWidthOrHeight方法其實就是對IE盒子模型的一個處理

    所以最終的尺寸值其實是

    offsetWidth/offsetHeigth + augmentWidthOrHeight方法

    複製程式碼

    return ( val +
                augmentWidthOrHeight(
                    elem,
                    name,
                    extra || ( isBorderBox ? "border" : "content" ),
                    valueIsBorderBox,
                    styles
                )
                ) + "px";

    複製程式碼

    augmentWidthOrHeight

    1.8增加了對css屬性box-sizing的支援,需要注意與1.7.2的區別了。

    1.7.2及以前的版本無論是否定義box-sizing: border-box返回的都是盒模型中元素內容的寬度或高度,不包括padding和border。

    augmentWidthOrHeight方法是特別針對盒子模型的處理

    例如,假如您需要並排放置兩個帶邊框的框,可通過將 box-sizing 設定為 "border-box"。這可令瀏覽器呈現出帶有指定寬度和高度的框,並把邊框和內邊距放入框中。

    複製程式碼

    <style>
            div { width:60px; height:60px; margin:5px; float:left; }
            #aaron-box-test {
                box-sizing: border-box;
                -moz-box-sizing: border-box;
                width: 500px;
                padding: 5px;
                border: 5px solid gold;
            }
        </style>
    </head>
    <body>
    <div id="aaron-box-test"></div>
    <script>
        var $el = $('#aaron-box-test')
        var w = $el.width(); //480
    </script>

    複製程式碼

    定義了

    box-sizing = border-box , width  = 480px

    box-sizing = content-box ,width = 500px

    IE6/7不支援box-sizing,輸出的依然是500。但支援該熟悉的瀏覽器此時輸出的結果則是480(刨去了盒模型的border和padding

    border遵循的是IE的標準,跟IE6一樣,將 border 與 padding 都包含在 width 之內

    當然盒子模型的好處也有的:無論如何改動 border 與 padding 的值,都不會導致 box 總尺寸發生變化,也就不會打亂頁面整體佈局。而在 Firefox 等現代瀏覽器下,如果我們要改變一下 padding 的值,就不得不重新計算 box 的 width

    innerWidth

    為匹配的元素集合中獲取第一個元素的當前計算寬度值,包括padding,但是不包括border。

    image

    這個方法返回元素的寬度,包括左邊和右邊的padding,單位是畫素。

    這個方法不適用於window and document物件,可以使用 代替。

    jQuery對CSS的處理統一呼叫了 jQuery.css( elem, type, extra ) 處理,不同的是這裡額外的提供一個引數extra

    當為innerWidth其實就滿足這一條件,所以 extra: "padding", 即額外還包行padding的處理

    對width操作是屬性有相容問題要處理的

    故而程式碼要走鉤子處理,此刻就是get方法,流程如上

    其中有比較大的hack

    比如Edwards大神的

    curCSS方法中用於在標準瀏覽器下轉換百分比值為更有用的畫素值

    整個流程程式碼看下來,你就發現jQuery的處理真的是細到極致,你可能就一個簡簡單單的width(), 實現的背後可能是幾百 上千行的程式碼量!!!

    如果覺得還有幫助的話,可以點一下右下角的【推薦一下】,希望能夠持續的為大家帶來好的技術文章!想跟我一起進步麼?那就【關注】我吧。