- .height()
- .innerHeight()
- .innerWidth()
- .outerHeight()
- .outerWidth()
- .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() 隨之改變,但是$(document).height()是不變的。
https://github.com/jquery/jquery/pull/764
window 反映的是視圖窗口,沒有用window.innerWidth
源碼是通過document.documentElement
return elem.document.documentElement[ "client" + name ];
因為有些樣式不是簡單的讀寫屬性就可以的,比如width就不是簡單地讀取el.style.width。為了解決這個問題,jquery定義了一個屬性 $.cssHooks,這里可以自定義對某個屬性的get和set操作。而且jquery中就是用cssHooks來處理某些特殊屬性
具體涉及了:
- borderWidth: Object
- height: Object
- margin: Object
- opacity: Object
- padding: Object
- 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。
這個方法返回元素的寬度,包括左邊和右邊的padding,單位是像素。
這個方法不適用於
window
anddocument
對象,可以使用.width()
代替。jQuery對CSS的處理統一調用了 jQuery.css( elem, type, extra ) 處理,不同的是這里額外的提供一個參數extra
當為innerWidth其實就滿足這一條件,所以 extra: "padding", 即額外還包行padding的處理
對width操作是屬性有兼容問題要處理的
故而代碼要走鈎子處理,此刻就是get方法,流程如上
其中有比較大的hack
比如Edwards大神的
curCSS方法中用於在標准瀏覽器下轉換百分比值為更有用的像素值
整個流程代碼看下來,你就發現jQuery的處理真的是細到極致,你可能就一個簡簡單單的width(), 實現的背后可能是幾百 上千行的代碼量!!!