在這個迷你係列的文章裏邊我將會解釋viewport,以及許多重要元素的寬度是如何工作的,比如<html>
元素,也包括窗口和屏幕。
這篇文章我們來聊聊關於移動瀏覽器的內容。如果你對移動開發完全是一個新手的話,我建議你先讀一下第一篇關於桌面瀏覽器的文章,先在熟悉的環境中進行下熱身。
移動瀏覽器的問題
當我們比較移動瀏覽器和桌面瀏覽器的時候,它們最顯而易見的不同就是屏幕尺寸。爲桌面瀏覽器所設計的網站在移動瀏覽器中顯示的內容明顯要少於在桌面瀏覽器中顯示的;不管是對其進行縮放直到文字小得無法閱讀,還是在屏幕中以合適的尺寸只顯示站點中的一小部分內容。
移動設備的屏幕比桌面屏幕要小得多;想想其最大有400px寬,有時候會小很多。(一些手機聲稱擁有更大的寬度,但是它在撒謊-或者也可以說它給我們提供了沒用的信息。)
平板設備中的像素中間層會在桌面環境和移動環境的缺口之間架起一段橋樑,比如像iPad或者傳說中HP基於webOS所研發的設備,但是這並沒有改變根本問題。站點必須也能在移動設備上工作,所以我們不得不讓它們能在小尺寸的屏幕上正常顯示。
最重要的問題在CSS上,特別是viewport的尺寸。如果我們照搬桌面環境的模式,那麼我們的CSS就要立馬熄火了(譯者:即顯示混亂)。
讓我們看下之前sidebar爲width: 10%
的例子。如果移動瀏覽器想要實現跟桌面瀏覽器一樣的行爲,它們最多爲元素設置40px的寬度,但是這太窄了。你的流式佈局會看起來被擠亂了。
解決這個問題的一個方法是爲移動瀏覽器建立一個特定的站點。先拋開你是否有必要這麼做這個基本問題,而實際的情況是隻有很少的網站擁有者真正知道要對移動設備做特殊的處理。
移動瀏覽器廠商想給它們的客戶儘可能的提供最好的體驗,這現在指的就是「儘可能的跟桌面一樣」。因此耍一些花招是必要的。
兩個viewport
viewport太窄了,以至於不能正常展示你的CSS佈局。明顯的解決方案是使viewport變寬一些。無論如何,需要把它分成兩部分:visual viewport和layout viewport。
George Cummins在Stack Overflow上對基本概念給出了最佳解釋:
把layout viewport想像成爲一張不會變更大小或者形狀的大圖。現在想像你有一個小一些的框架,你通過它來看這張大圖。(譯者:可以理解爲「管中窺豹」)這個 小框架的周圍被不透明的材料所環繞,這掩蓋了你所有的視線,只留這張大圖的一部分給你。你通過這個框架所能看到的大圖的部分就是visual viewport。當你保持框架(縮小)來看整個圖片的時候,你可以不用管大圖,或者你可以靠近一些(放大)只看局部。你也可以改變框架的方向,但是大圖 (layout viewport)的大小和形狀永遠不會變。
也看一下Chris給出的解釋。
visual viewport是頁面當前顯示在屏幕上的部分。用戶可以通過滾動來改變他所看到的頁面的部分,或者通過縮放來改變visual viewport的大小。
HTML5: 兩個viewport的故事(第二部分)
無論怎樣,CSS佈局,尤其是百分比寬度,是以layout viewport做爲參照系來計算的,它被認爲要比visual viewport寬。
所以<html>
元素在初始情況下用的是layout viewport的寬度,並且你的CSS是在屏幕(譯者注:寬度等於layout viewport的虛擬屏幕)好像明顯比電話屏幕寬(物理屏幕)要寬的假設基礎上進行解釋的。這使得你站點佈局的行爲與其在桌面瀏覽器上的一樣。
layout viewport有多寬?每個瀏覽器都不一樣。Safari iPhone爲980px,Opera爲850px,Android WebKit爲800px,最後IE爲974px。
一些瀏覽器有特殊的行爲:
- Symbian WebKit會保持layout viewport與visual viewport相等,是的,這意味着擁有百分比寬度元素的行爲可能會比較奇怪。但是,如果頁面由於設置了絕對寬度而不能放入visual viewport中,那麼瀏覽器會把layout viewport拉伸到最大850px寬。
- Samsung WebKit (on bada)使layout viewport和最寬的元素一樣寬。
- 在BlackBerry上,layout viewport在100%縮放比例的情況下等於visual viewport。這不會變。
縮放
很顯然兩個viewport都是以CSS像素度量的。但是當進行縮放(如果你放大,屏幕上的CSS像素會變少)的時候,visual viewport的尺寸會發生變化,layout viewport的尺寸仍然跟之前的一樣。(如果不這樣,你的頁面將會像百分比寬度被重新計算一樣而經常被重新佈局。)
理解layout viewport
爲了理解layout viewport的尺寸,我們不得不看一下當頁面被完全縮小後會發生什麼。許多移動瀏覽器會在初始情況下以完全縮小的模式來展示任何頁面。
重點是:瀏覽器已經爲自己的layout viewport選擇了尺寸,這樣的話它在完全縮小模式的情況下完整的覆蓋了屏幕(並且等於visual viewport)。
HTML5: 兩個viewport的故事(第二部分)
所以layout viewport的寬度和高度等於在最大限度縮小的模式下屏幕上所能顯示的任何內容的尺寸。當用戶放大的時候這些尺寸保持不變。
HTML5: 兩個viewport的故事(第二部分)
layout viewport寬度一直是一樣的。如果你旋轉你的手機,visual viewport會發生變化,但是瀏覽器通過輕微的放大來適配這個新的朝向,所以layout viewport又和visual viewport一樣寬了。
HTML5: 兩個viewport的故事(第二部分)
這對layout viewport的高度會有影響,現在的高度比肖像模式(豎屏)要小。但是web開發者不在乎高度,只在乎寬度。
HTML5: 兩個viewport的故事(第二部分)
度量layout viewport
我們現在有兩個需要度量的viewport。很幸運的是瀏覽器戰爭給我們提供了兩個屬性對。
document.documentElement.clientWidth
和-Height
包含了layout viewport的尺寸。
document.documentElement.clientWidth/Height
- 意義:Layout viewport的尺寸
- 度量單位:CSS像素
- 完全支持Opera, iPhone, Android, Symbian, Bolt, MicroB, Skyfire, Obigo。
- 在Iris中Visual viewport有問題
- Samsung WebKit在頁面應用了
<meta viewport>
標籤的時候會返回正確的值;否則使用<html>
元素的尺寸。 - Firefox返回以設備像素爲單位的屏幕尺寸。
- IE返回1024×768。然而,它把信息存儲在
document.body.clientWidth/Height
中。這和桌面的IE6是一致的。 - NetFront的值只在100%縮放比例的情況下是正確的。
- Symbian WebKit 1 (老的S60v3設備)不支持這些屬性。
- Samsung WebKit在頁面應用了
- BlackBerry不支持。
HTML5: 兩個viewport的故事(第二部分)
朝向會對高度產生影響,但對寬度不會產生影響。
HTML5: 兩個viewport的故事(第二部分)
度量visual viewport
對於visual viewport,它是通過window.innerWidth/Height
來進行度量的。很明顯當用戶縮小或者放大的時候,度量的尺寸會發生變化,因爲屏幕上的CSS像素會增加或者減少。
window.innerWidth/Height
- 意義:Visual viewport的尺寸。
- 度量單位:CSS像素。
- 完全支持iPhone,Symbian,BlackBerry。
- 問題
- Opera和Firefox返回以設備像素爲單位的屏幕寬度。
- Android,Bolt,MicroB和NetFront返回以CSS像素爲單位的layout viewport尺寸。
- 不支持IE,但是它在
document.documentElement.offsetWidth/Height
中提供visual viewport的尺寸。- Samsung WebKit返回的是layout viewport或者
<html>
的尺寸,這取決於頁面是否應用了<meta viewport>
標籤。
- Samsung WebKit返回的是layout viewport或者
- Iris,Skyfire,Obigo根本就是扯淡。
HTML5: 兩個viewport的故事(第二部分)
不幸的是這是瀏覽器不兼容問題中的一部分;許多瀏覽器仍然不得不增加對visual viewport度量尺寸的支持。但是沒有瀏覽器把這個度量尺寸存放任何其他的屬性對中,所以我猜window.innerWidth/Height
是標準,儘管它被支持的很糟。
屏幕
像桌面環境一樣,screen.width/height
提供了以設備像素爲單位的屏幕尺寸。像在桌面環境上一樣,做爲一個開發者你永遠不需要這個信息。你對屏幕的物理尺寸不感興趣,而是對屏幕上當前有多少CSS像素感興趣。
screen.width and screen.height
- 意義:屏幕尺寸
- 度量單位:設備像素
- 完全支持Opera Mini,Android,Symbian,Iris,Firefox,MicroB,IE,BlackBerry。
- 問題:Windows Mobile上的Opera Mobile只提供了風景模式(橫屏)的尺寸。S60上的Opera Mobile返回的值是正確的。
- Samsung WebKit返回layout viewport或者
<html>
的尺寸,這取決於是否在頁面上應用了<meta viewport>
標籤。 - iPhone和Obigo只提供了肖像模式(豎屏)的尺寸。
- NetFront只提供風景模式(橫屏)的尺寸。
- Samsung WebKit返回layout viewport或者
- Bolt,Skyfire依舊在扯淡。
HTML5: 兩個viewport的故事(第二部分)
縮放比例 zoom level
直接讀出縮放比例是不可能的,但是你可以通過以screen.width
除以window.innerWidth
來獲取它的值。當然這只有在兩個屬性都被完美支持的情況下才有用。
幸運的是縮放比例並不太重要。你需要知道的是當前屏幕上有多少個CSS像素。你可以通過window.innerWidth
來獲取這個信息,如果它被正確支持的話。
滾動距離Scrolling offset
你還需知道的是visual viewport當前相對於layout viewport的位置。這是滾動距離,並且就像在桌面一樣,它被存儲在window.pageX/YOffset
之中。
window.pageX/YOffset
- 意義:滾動距離;與visual viewport相對於layout viewport的距離一樣。
- 度量單位:CSS像素
- 完全支持iPhone,Android,Symbian,Iris,MicroB,Skyfire,Obigo。
- 問題:Opera,Bolt,Firefox和NetFront一直返回0。
- Samsung WebKit只有當
<meta viewport>
被應用到頁面上時候才返回正確的值。
- Samsung WebKit只有當
- 不支持IE,BlackBerry。IE把值存在
document.documentElement.scrollLeft/Top
之中。
HTML5: 兩個viewport的故事(第二部分)
\<html> 元素
就像在桌面上一樣,document.documentElement.offsetWidth/Height
提供了以CSS像素爲單位的<html>
元素的整個尺寸。
document.documentElement.offsetWidth/Height
- 意義:
<html>
元素的整體尺寸。 - 度量單位:CSS像素。
- 完全支持Opera,iPhone,Android,Symbian,Samsung,Iris,Bolt,Firefox,MicroB,Skyfire,BlackBerry,Obigo。
- 問題:NetFront的值只在100%縮放比例的情況下才正確。
- IE使用這個屬性對來存儲visual viewport的尺寸。在IE中,去
document.body.clientWidth/Height
中獲取正確的值。
- IE使用這個屬性對來存儲visual viewport的尺寸。在IE中,去
HTML5: 兩個viewport的故事(第二部分)
媒體查詢Media queries
媒體查詢和其在桌面環境上的工作方式一樣。width/height
使用layout viewport做爲參照物,並且以CSS像素進行度量,device-width/height
使用設備屏幕,並且以設備像素進行度量。
換句話說,width/height
是document.documentElement.clientWidth/Height
值的鏡像,同時device-width/height
是screen.width/height
值的鏡像。(它們在所有瀏覽器中實際上就是這麼做的,即使這個鏡像的值不正確。)
媒體查詢
- 意義:度量
<html>
元素的寬度(CSS像素)或者設備寬度(設備像素)。 - 完全支持Opera,iPhone,Android,Symbian,Samsung,Iris,Bolt,Firefox,MicroB。
- 不支持Skyfire,IE,BlackBerry,NetFront,Obigo。
- 注意我在這裏測試的是瀏覽器是否能從正確的「屬性對」獲取它們的數據。這些屬性對是否提供正確的信息不是這個測試的一部分。
HTML5: 兩個viewport的故事(第二部分)
現在哪個度量的尺寸對web開發者更有用?我的觀點是,不知道。
我開始認爲device-width
是最重要的那一個,因爲它給我們提供了關於我們可能會使用的設備的一些信息。比如,你可以根據設備的寬度來更改你的佈局的寬度。不過,你也可以使用<meta viewport>
來做這件事情;使用device-width
媒體查詢並不是絕對必要的。
那麼width
究竟是不是更重要的媒體查詢呢?可能是;它提供了某些線索,這些線索是關於瀏覽器廠商認爲在這個設備上網站應該有的正確寬度。但是這有些模糊不清,並且width
媒體查詢實際上不提供任何其他信息。
所以我不做選擇。目前我認爲媒體查詢在分辨你是否在使用桌面電腦,平板,或者移動設備方面很重要,但是對於區分各種平板或者移動設備並沒有什麼用。
或者還有其他用處。
事件座標
這裏的事件座標與其在桌面環境上的工作方式差不多。不幸的是,在十二個測試過的瀏覽器中只有Symbian WebKit和Iris這兩個瀏覽器能獲取到三個完全正確的值。其他所有瀏覽器都或多或少有些嚴重的問題。
pageX/Y
仍然是相對於頁面,以CSS像素爲單位,並且它是目前爲止三個屬性對中最有用的,就像它在桌面環境上的那樣。
Event coordinates
- 意義:見正文
- 度量單位:見正文
- 完全支持Symbian,Iris
- 問題:Opera Mobile在三個屬性對中提供的都是pageX/Y的值,但是當你滾動一段距離後就出問題了。
- 在iPhone,Firefox和BlackBerry上clientX/Y等於pageX/Y。
- 在Android和MicroB上screenX/Y等於clientX/Y(換句話說,也就是以CSS像素爲單位)。
- 在Firefox上screenX/Y是錯的。
- IE,BlackBerry和Obigo不支持pageX/Y。
- 在NetFront上三個屬性對的值都等於screenX/Y。
- 在Obigo上clientX/Y等於screenX/Y。
- Samsung WebKit一直返回pageX/Y。
- 沒有在Opera Mini,Bolt,Skyfire上測試過。
HTML5: 兩個viewport的故事(第二部分)
clientX/Y
是相對於visual viewport來計算,以CSS像素爲單位的。這有道理的,即使我還不能完全指出這麼做的好處。
screenX/Y
是相對於屏幕來計算,以設備像素爲單位。當然,這和clientX/Y
用的參照系是一樣的,並且設備像素在這沒有用處。所以我們不需要擔心screenX/Y
;跟在桌面環境上一樣沒有用處。
HTML5: 兩個viewport的故事(第二部分)
viewport meta標籤
Meta viewport
- 意義:設置layout viewport的寬度。
- 度量單位:CSS像素。
- 完全支持Opera Mobile,iPhone,Android,Iris,IE,BlackBerry,Obigo。
- 不支持Opera Mini,Symbian,Bolt,Firefox,MicroB,NetFront。
- 問題:Skyfire不能處理我的測試頁面。
- 如果在Samsung WebKit中對頁面應用
<meta viewport>
,一些其他屬性的意義會發生變化。 - Opera Mobile,iPhone,Samsung和BlackBerry不允許用戶進行縮小。
- 如果在Samsung WebKit中對頁面應用
最後,讓我們討論一下<meta name="viewport" content="width=320">
;起初它是蘋果做的一個擴展,但是與此同時被更多的瀏覽器所借鑑。它的意思是調整layout viewport的大小。爲了理解爲什麼這麼做是必要的,讓我們後退一步。
假設你創建了一個簡單的頁面,並且沒有給你的元素設置「寬度」。那麼現在它們會被拉伸來填滿layout viewport寬度的100%。大部分瀏覽器會進行縮放從而在屏幕上展示整個layout viewport,產生下面這樣的效果:
HTML5: 兩個viewport的故事(第二部分)
所有用戶將會立刻進行放大操作,這個是工作的,但是大部分瀏覽器完好無缺的保持元素的寬度,這使得文字很難閱讀。
HTML5: 兩個viewport的故事(第二部分)
(值得注意的例外是Android WebKit,它實際上會減小包含文字的元素的大小,所以文字就能適配屏幕。這簡直太有才了,我覺得所有其他瀏覽器應該借鑑這個行爲。我過陣子將會完整的寫一下這個議題。)
現在你應該嘗試設置html {width: 320px}
。現在<html>
元素收縮了,並且其他元素現在使用的是320px的100%。這在用戶進行放大操作的時候有用,但是在初始狀態是沒用的,當用戶面對一個縮小了的頁面這幾乎不包含任何內容。
HTML5: 兩個viewport的故事(第二部分)
爲了繞開這個問題蘋果發明了viewport meta標籤。當你設置<meta name="viewport" content="width=320">
的時候,你就設置了layout viewport的寬度爲320px。現在頁面的初始狀態也是正確的。
HTML5: 兩個viewport的故事(第二部分)
你可以把layout viewport的寬度設置爲任何你想要的尺寸,包括device-width
。device-width
會把screen.width
(以設備像素爲單位)做爲其值,並相應的重置layout viewport的尺寸。
但這裏有一個隱情。有時候正規的screen.width
不那麼明瞭,因爲像素的數量太大了。比如,Nexus One的正規寬度是480px,但是Google的工程師們覺得當使用device-width
的時候,layout viewport的寬度爲480px,這有些太大了。他們把寬度縮小爲三分之二,所以device-width
會返回給你一個320px的寬度,就像在iPhone上一樣。
如果,像傳聞那樣,新的iPhone將會炫耀一個更大的像素數量(並不意味着一個更大的屏幕),如果蘋果借鑑了這個行爲我將不會感到驚訝。也許最終device-width
就意味着320px。
相關研究
一些相關的主題不得不需要進行更深一步的研究:
position: fixed
。一個固定的元素,就像我們知道的那樣,是相對於viewport來進行定位的。但是相對於哪個viewport?我正在同時做這個研究。- 其他媒體查詢:dpi,orientation,aspect-ratio。尤其是dpi,那是一個災難地區,不僅僅是因爲所有瀏覽器都返回96dpi,通常都是錯的,也是因爲我完全不確定對於web開發者來說哪個值是他們最感興趣的。
- 當一個元素比layout viewport/HTML元素寬的時候會發生什麼?比如我把一個擁有
width: 1500px
屬性的元素插入到我的測試頁面中的一個?這個元素將會從HTML元素中伸出來(overflow: visible
),但這意味着實際的viewport可以變得比layout viewport要寬。除了這個以外,舊Android(Nexus One)還會當這個發生的時候放大HTML元素。這是個好主意嗎?
(完)