jQuery2.0.3原始碼分析系列之(29) 視窗尺寸
基礎回顧
一般的,在獲取瀏覽器視窗的大小和位置時,有以下幾個屬性可以使用:
在不同的瀏覽器中,以下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來處理某些特殊屬性
具體涉及了:
- 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
and document
物件,可以使用 代替。
jQuery對CSS的處理統一呼叫了 jQuery.css( elem, type, extra ) 處理,不同的是這裡額外的提供一個引數extra
當為innerWidth其實就滿足這一條件,所以 extra: "padding", 即額外還包行padding的處理
對width操作是屬性有相容問題要處理的
故而程式碼要走鉤子處理,此刻就是get方法,流程如上
其中有比較大的hack
比如Edwards大神的
curCSS方法中用於在標準瀏覽器下轉換百分比值為更有用的畫素值
整個流程程式碼看下來,你就發現jQuery的處理真的是細到極致,你可能就一個簡簡單單的width(), 實現的背後可能是幾百 上千行的程式碼量!!!
如果覺得還有幫助的話,可以點一下右下角的【推薦一下】,希望能夠持續的為大家帶來好的技術文章!想跟我一起進步麼?那就【關注】我吧。