兩個viewport的故事(第一部分)

發表於2013-07-29

在這個迷你係列的文章裡邊我將會解釋viewport,以及許多重要元素的寬度是如何工作的,比如<html>元素,也包括視窗和螢幕

這篇文章是關於桌面瀏覽器的,其唯一目的就是為移動瀏覽器中相似的討論做個鋪墊。大部分開發者憑直覺已經明白了大部分桌面瀏覽器中的概念。在移動端我們將會接觸到相同的概念,但是會更加複雜,所以對大家已經知道的術語做個提前的討論將會對你理解移動瀏覽器產生巨大的幫助。

 

概念:裝置畫素和CSS畫素

你需要明白的第一個概念是CSS畫素,以及它和裝置畫素的區別。

裝置畫素是我們直覺上覺得「靠譜」的畫素。這些畫素為你所使用的各種裝置都提供了正規的解析度,並且其值可以(通常情況下)從screen.width/height屬性中讀出。

如果你給一個元素設定了width: 128px的屬性,並且你的顯示器是1024px寬,當你最大化你的瀏覽器螢幕,這個元素將會在你的顯示器上重複顯示8次(大概是這樣;我們先忽略那些微妙的地方)。

如果使用者進行縮放,那麼計算方式將會發生變化。如果使用者放大到200%,那麼你的那個擁有width: 128px屬性的元素在1024px寬的顯示器上只會重複顯示4次。

現代瀏覽器中實現縮放的方式無怪乎都是「拉伸」畫素。所以,元素的寬度並沒有從128被修改為256畫素;相反是實際畫素被放大了兩倍。形式上,元素仍然是128個CSS畫素寬,即使它佔據了256個裝置畫素的空間。

換句話說,放大到200%使一個CSS畫素變成一個裝置畫素的四倍。(寬度2倍,高度2倍,總共4倍)

一些圖片可以解釋清楚這個概念。這兒有四個100%縮放比的元素。這兒沒有什麼值得看的;CSS畫素與裝置畫素完全重疊。

現在讓我們縮小。CSS畫素開始收縮,這意味著現在一個裝置畫素覆蓋了多個CSS畫素。

如果你進行放大,相反的行為會發生。CSS畫素開始變大,現在一個CSS畫素覆蓋了多個裝置畫素。

這兒的要點是你只對CSS畫素感興趣。這些就是那些控制你的樣式表如何被渲染的畫素。

裝置畫素對你(譯者:指的是開發者)來說基本上沒用。但是對於使用者不一樣;使用者將會放大或者縮小頁面直到他能舒服的閱讀為止。無論怎樣,縮放等級對你不會產生影響。瀏覽器將會自動的使你的CSS佈局被拉伸或者被壓縮。

 

100%縮放

我是以假設縮放等級為100%來開始這個例子的。是時候需要更加嚴格的來定義一下這個100%了:

100%縮放的概念在接下來的解釋中會非常有用,但是在你的日常工作中你不用過分的擔心它。在桌面環境上你將會在100%縮放等級的情況下測試你的站點,但即使使用者放大或者縮小,CSS畫素的魔力將會保證你的佈局保持相同的比率。

 

螢幕尺寸

screen.width/height

  • 意義:使用者螢幕的整體大小。
  • 度量單位:裝置畫素。
  • 瀏覽器錯誤:IE8以CSS畫素對其進行度量,IE7和IE8模式下都有這個問題。

讓我們看一些實用的度量。我們將會以screen.widthscreen.height做為開始。它們包括使用者螢幕的整個寬度和高度。它們的尺寸是以裝置畫素來進行度量的,因為它們永遠不會變:它們是顯示器的屬性,而不是瀏覽器的。

Fun! 但是這些資訊跟對我們有什麼用呢?

基本上沒用。使用者的顯示器尺寸對於我們來說不重要-好吧,除非你想度量它來豐富你的web統計資料庫。

 

視窗尺寸

window.innerWidth/Height

  • 意義:瀏覽器視窗的整體大小,包括滾動條。
  • 度量單位:CSS畫素。
  • 瀏覽器錯誤:IE7不支援。Opera以裝置畫素進行度量。

相反,你想知道的是瀏覽器視窗的內部尺寸。它告訴了你使用者到底有多少空間可以用來做CSS佈局。你可以通過window.innerWidthwindow.innerHeight來獲取這些尺寸。

很顯然,視窗的內部寬度是以CSS畫素進行度量的。你需要知道你的佈局空間中有多少可以擠進瀏覽器視窗,當使用者放大的時候這個數值會減少。所以如果使用者進行放大操作,那麼在視窗中你能獲取的空間將會變少,window.innerWidth/Height的值也變小了。 (這兒的例外是Opera,當使用者放大的時候window.innerWidth/Height並沒有減少:它們是以裝置畫素進行度量的。這個問題在桌面上是比較煩人的,但是就像我們將要看到的,這在移動裝置上是非常嚴重的。)

注意度量的寬度和高度是包括滾動條的。它們也被視為內部視窗的一部分。(這大部分是因為歷史原因造成的。)

 

滾動距離

window.pageX/YOffset

  • 意義:頁面滾動的距離。
  • 度量:CSS畫素。
  • 瀏覽器錯誤:無。

window.pageXOffsetwindow.pageYOffset,包含了文件水平和垂直方向的滾動距離。所以你可以知道使用者已經滾動了多少距離。

這些屬性也是以CSS畫素進行度量的。你想知道的是文件已經被滾動了多長距離,不管它是放大還是縮小的狀態。

理論上,如果使用者向上滾動,然後放大,window.pageX/YOffset將會發生變化。但是,瀏覽器為了想保持web頁面的連貫,會在使用者縮放的時候保持相同的元素位於可見頁面的頂部。這個機制並不能一直很完美的執行,但是它意味著在實際情況下window.pageX/YOffset並沒有真正的更改:被滾動出視窗的CSS畫素的數量仍然(大概)是相同的。

 

概念:viewport

在我們繼續介紹更多的JavaScript屬性之前,我們必須介紹另一個概念:viewport。

viewport的功能是用來約束你網站中最頂級包含塊元素(containing block)<html>的。

這聽起來有一點模糊,所以看一個實際的例子。假設你有一個流式佈局,並且你眾多邊欄中的一個具有width: 10%屬性。現在這個邊欄會隨著瀏覽器視窗大小的調整而恰好的放大和收縮。但是這到底是如何工作的呢?

從技術上來說,發生的事情是邊欄獲取了它父元素寬度的10%。比方說是<body>元素(並且你還沒有給它設定過寬度)。所以問題就變成了<body>的寬度是哪個?

普通情況下,所有塊級元素使用它們父元素寬度的100%(這兒有一些例外,但是讓我們現在先忽略它)。所以<body>元素和它的父元素<html>一樣寬。

那麼<html>元素的寬度是多少?它的寬度和瀏覽器視窗寬度一樣。這就是為什麼你的那個擁有width: 10%屬性的側邊欄會佔據整個瀏覽器視窗的10%。所有web開發者都很直觀的知道並且在使用它。

你可能不知道的是這個行為在理論上是如何工作的。理論上,<html>元素的寬度是被viewport的寬度所限制的。<html>元素使用viewport寬度的100%。

viewport,接著,實際上等於瀏覽器視窗:它就是那麼定義的。viewport不是一個HTML結構,所以你不能用CSS來改變它。它在桌面環境下只是擁有瀏覽器視窗的寬度和高度。在移動環境下它會有一些複雜。

 

後果 Consequences

這個狀況會有產生一些異樣的後果。你可以在這個站點看到這些後果中的一個。滾動到頂部,然後放大兩次或者三次,之後這個站點的內容就從瀏覽器視窗溢位了。

現在滾動到右邊,然後你將會看見站點頂部的藍色邊欄不再覆蓋一整行了。

這個行為是由於viewport的定義方式而產生的一個後果。我之前給頂部的藍色邊欄設定了width: 100%。什麼的100%?<html>元素的100%,它的寬度和viewport是一樣的,viewport的寬度是和瀏覽器視窗一樣的。

問題是:在100%縮放的情況下這個工作的很好,現在我們進行了放大操作,viewport變得比我的站點的總體寬度要小。這對於viewport它本身來說沒什麼影響,內容現在從<html>元素中溢位了,但是那個元素擁有overflow: visible,這意味著溢位的內容在任何情況下都將會被顯示出來。

但是藍色邊欄並沒有溢位。我之前給它設定了width: 100%,並且瀏覽器把viewport的寬度賦給了它。它們根本就不在乎現在寬度實在是太窄了。

 

文件寬度?

我真正需要知道的是頁面中全部內容的寬度是多少,包括那些「伸出」的部分。據我所知得到這個值是不可能的(好吧,除非你去計算頁面上所有元素的寬度和邊距,但是委婉的說,這是容易出錯的)。

我開始相信我們需要一個我稱其為「文件寬度」(document width,很顯然用CSS畫素進行度量)的JavaScript屬性對。

並且如果我們真的如此時髦,為什麼不把這個值引入到CSS中?我將會給我的藍色邊欄設定width: 100%,此值基於文件寬度,而不是<html>元素的寬度。(但是這個很複雜,並且如果不能實現我也不會感到驚訝。)

瀏覽器廠商們,你們怎麼認為的?

 

度量viewport

document.documentElement.clientWidth/Height

  • 意義:Viewport尺寸。
  • 度量:CSS畫素。
  • 瀏覽器錯誤:無。

你可能想知道viewport的尺寸。它們可以通過document.documentElement.clientWidth-Height得到。

如果你瞭解DOM,你應該知道document.documentElement實際上指的是<html>元素:即任何HTML文件的根元素。可以說,viewport要比它更高一層;它是包含<html>元素的元素。如果你給<html>元素設定width屬性,那麼這將會產生影響。(我不推薦這麼做,但是那是可行的。)

在那種情況下document.documentElement.clientWidth-Height給出的仍然是viewport的尺寸,而不是<html>元素的。(這是一個特殊的規則,只對這個元素的這個屬性對產生作用。在任何其他的情況下,使用的是元素的實際寬度。)

所以document.documentElement.clientWidth-Height一直代表的是viewport的尺寸,不管<html>元素的尺寸是多少。

 

兩個屬性對

但是難道viewport寬度的尺寸也可以通過window.innerWidth/Height來提供嗎?怎麼說呢,模稜兩可。

兩個屬性對之間存在著正式區別:document.documentElement.clientWidth-Height並不包含滾動條,但是window.innerWidth/Height包含。這像是雞蛋裡挑骨頭。

事實上兩個屬性對的存在是瀏覽器戰爭的產物。當時Netscape只支援window.innerWidth/Height,IE只支援document.documentElement.clientWidthHeight。從那時起所有其他瀏覽器開始支援clientWidth/Height,但是IE沒有支援window.innerWidth/Height

在桌面環境上擁有兩個屬性對是有一些累贅的 - 但是就像我們將要看到的,在移動端這將會得到祝福。

 

度量<html>元素

document.documentElement.offsetWidth/Height

  • 意義:元素(也就是頁面)的尺寸。
  • 度量:CSS畫素。
  • 瀏覽器錯誤:IE度量的是viewport,而不是元素。

所以clientWidth/Height在所有情況下都提供viewport的尺寸。但是我們去哪裡獲取<html>元素本身的尺寸呢?它們儲存在document.documentElement.offsetWidth-Height之中。

這些屬性可以使你以塊級元素的形式訪問<html>元素;如果你設定width,那麼offsetWidth將會表示它。

 

事件中的座標

pageX/Y, clientX/Y, screenX/Y

  • 意義:見正文。
  • 度量單位:見正文。
  • 瀏覽器錯誤:IE不支援pageX/Y。IE和Opera以CSS畫素為單位計算screenX/Y。

然後是事件中的座標。當一個滑鼠事件發生時,有不少於五種屬性對可以給你提供關於事件位置的資訊。對於我們當前的討論來說它們當中的三種是重要的:

  • pageX/Y提供了相對於<html>元素的以CSS畫素度量的座標。

  • clientX/Y提供了相對於viewport的以CSS畫素度量的座標。

  • screenX/Y提供了相對於螢幕的以裝置畫素進行度量的座標。

90%的時間你將會使用pageX/Y;通常情況下你想知道的是相對於文件的事件座標。其他的10%時間你將會使用clientX/Y。你永遠不需要知道事件相對於螢幕的座標。

 

媒體查詢

媒體查詢

  • 意義:見正文。
  • 度量單位:見正文。
  • 瀏覽器錯誤:IE不支援它們。
    • 如果 device-width/height是以CSS畫素進行度量的,那麼Firefox將會使用screen.width/height的值。
    • 如果width/height是以裝置畫素進行度量的,那麼Safari和Chrome將會使用documentElement.clientWidth/Height的值。

最後,說說關於媒體查詢的事。原理很簡單:你可以宣告「只在頁面寬度大於,等於或者小於一個特定尺寸的時候才會被執行」的特殊的CSS規則。比如:

當前sidebar是300px寬,除了當寬度小於400px的時候,在那種情況下sidebar變得100px寬。

問題很顯然:我們這兒度量的是哪個寬度?

這兒有兩個對應的媒體查詢:width/heightdevice-width/device-height

  1. width/height使用和documentElement .clientWidth/Height(換句話說就是viewport寬高)一樣的值。它是工作在CSS畫素下的。
  2. device-width/device-height使用和screen.width/height(換句話說就是螢幕的寬高)一樣的值。它工作在裝置畫素下面。

你應該使用哪個?這還用想?當然是width。Web開發者對裝置寬度不感興趣;這個是瀏覽器視窗的寬度。

所以在桌面環境下去使用width而去忘記device-width吧。我們即將看到這個情況在移動端會更加麻煩。

 

總結

本文總結了我們對桌面瀏覽器行為的探尋。這個系列的第二部分把這些概念指向了移動端,並顯示的指出了與桌面環境上的一些重要區別。

相關文章