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

原文地址:http://weizhifeng.net/viewports.html

在這個迷你係列的文章裏邊我將會解釋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像素與設備像素完全重疊。

csspixels_100

現在讓我們縮小。CSS像素開始收縮,這意味着現在一個設備像素覆蓋了多個CSS像素。

csspixels_out

如果你進行放大,相反的行爲會發生。CSS像素開始變大,現在一個CSS像素覆蓋了多個設備像素。

csspixels_in

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

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

100%縮放

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

在縮放比例100%的情況下一個CSS像素完全等於一個設備像素。

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

屏幕尺寸

screen.width/height

  • 意義:用戶屏幕的整體大小。
  • 度量單位:設備像素。
  • 瀏覽器錯誤:IE8以CSS像素對其進行度量,IE7和IE8模式下都有這個問題。

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

desktop_screen

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

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

窗口尺寸

window.innerWidth/Height

  • 意義:瀏覽器窗口的整體大小,包括滾動條。
  • 度量單位:CSS像素。
  • 瀏覽器錯誤:IE7不支持。Opera以設備像素進行度量。

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

desktop_inner

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

desktop_inner_zoomed

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

滾動距離

window.pageX/YOffset

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

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

desktop_page

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

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

desktop_page_zoomed

概念: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

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

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

desktop_htmlbehaviour

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

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

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

desktop_100percent

文檔寬度?

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

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

desktop_documentwidth

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

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

度量viewport

document.documentElement.clientWidth/Height

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

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

desktop_client

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

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

desktop_client_smallpage

所以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之中。

desktop_offset

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

desktop_offset_smallpage

事件中的座標

pageX/Y, clientX/Y, screenX/Y

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

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

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

desktop_pageXY

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

desktop_clientXY

  • screenX/Y提供了相對於屏幕的以設備像素進行度量的座標。

desktop_screenXY

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

媒體查詢

媒體查詢

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

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

div.sidebar {
    width: 300px;
}

@media all and (max-width: 400px) {
    // styles assigned when width is smaller than 400px;
    div.sidebar {
        width: 100px;
    }

}

當前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(換句話說就是屏幕的寬高)一樣的值。它工作在設備像素下面。

desktop_mediaqueries

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

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

總結

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

(完)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章