如何更愉快地使用em —— 別說你懂CSS相對單位
來自專欄集異璧22 人贊了文章
前段時間試譯了Keith J.Grant的CSS好書《CSS in Depth》,其中的第二章《Working with relative units》,書中對relative units的講解和舉例可以說相當全面,看完之後發現自己並不太懂CSS相對單位,也希望分享給大家,所以有了這個譯文系列。(若有勘誤或翻譯建議,歡迎 Github PR ^_^)
《別說你懂CSS相對單位》系列譯文:
- 如何更愉快地使用em [本文]
- 如何更愉快地使用rem
- 視口相關單位的應用
- 無單位數字和行高
- CSS自定義屬性
本文對應的章節目錄:
- 2.1 相對單位值的魔力
- 2.1.1 完美像素設計(pixel-perfect design)的掙扎
- 2.1.2 完美像素網頁的終結
- 像素(pixel)、點(point)和pc(pica)
- 2.2 em和rem
- 2.2.1 對font-size使用em
- 當我們在一個元素內用em同時聲明font-size和其他屬性
- 字型大小收縮問題
CSS提供了很多種方式去定義一個值。大家最熟悉的可能也是最容易使用的就是像素(pixel),這被稱做「絕對單位」。也就是說,5px在不同的場景下是一樣的值。而其他的單位,如em和rem,不是絕對的而是相對的。相對單位的值會根據外部影響因素的變化而變化。例如,2em的值取決於你在哪個元素使用它(有時甚至是哪個屬性)。很自然,相對單位使用起來會比較困難。
開發人員,甚至有經驗的CSS開發人員,往往不喜歡跟相對單位打交道,其中包括臭名昭著的em。em的值可以被改變的方式似乎難以預測,沒有px那麼清晰。在本章中,我將揭開相對單位的神秘面紗。首先,我會解釋它們為CSS帶來的獨特價值,然後我會幫助你更好地理解它們。我會解釋它們的工作原理,也會告訴你怎麼征服它們那看似不可預測的特性。你可以讓相對單位為你所用,正確地運行,它們將讓你的代碼變得更加簡單、靈活和容易使用。
2.1 相對單位值的魔力
CSS是通過遲邦定(late-binding)的方式把樣式渲染到web頁面上的:內容和它的樣式會在各自的渲染完成之後再合併到一起。比起其他類型的圖形設計,這給設計過程添加了它們沒有的複雜程度,同時也賦予CSS更強大的能力 —— 一個樣式表可供成百上千個頁面使用。此外,用戶可以直接改變頁面的最終呈現方式。舉個例子,用戶可以更改默認字型大小大小或者調整瀏覽器窗口的大小。
在早期的計算機應用程序開發以及傳統出版行業中,開發人員或出版商清楚知道所在的媒介存在哪些限制。對於一個特定的應用程序,窗口可能是400px寬,300px高,或者一個版面可能是4英寸寬,6?英寸高。因此,當開發人員布局應用程序的按鈕和文本時,他們很清楚這些元素可以做成什麼尺寸,以及在屏幕上還有多少空間可以留給他們用來處理其他元素。然而在網頁上,情況卻不是這樣的。
2.1.1 完美像素設計(pixel-perfect design)的掙扎
在web環境下,用戶可以將瀏覽器窗口設置為任意大小,且CSS需要去適應它。另外,用戶可以在一個頁面打開後,再調整它的大小,CSS也需要去適應這些新的約束條件。這說明了在你創建頁面時樣式還沒有被調用,而是當頁面在屏幕上渲染時,瀏覽器才會去計算樣式的規則。
這給CSS增加了一層抽象的概念。我們不應該根據理想的情境來設計元素,而是應該聲明一些樣式規則,可以讓該元素在任何場景下都能跑通。對於現在的互聯網,你的頁面可能要在一個4英寸的手機屏幕上展示,也可能在一個30英寸的大屏幕上。
長久以來,設計師大量使用「完美像素」設計,緩解了這個問題帶來的複雜性。他們會創建一個有著嚴格定義的容器,通常是一個大約800px寬的居中的縱向列。然後在這些限制下他們再進行設計,這跟他們的前輩在原生應用程序或印刷出版物中做的設計或多或少有點類似。
2.1.2 完美像素網頁的終結
隨著技術的進步和製造商推出更高解析度的顯示器,像素完美的設計方式慢慢開始崩潰。在21世紀初期,把頁面設計成1024px寬還是800px寬,哪個是更保險的展示策略?開發者針對這個問題討論得很多。然後,我們又針對能否改成1280px寬有類似的討論。是時候做個決定了。把我們網站的內容寬度做得寬一點(相對於落伍的小電腦屏幕),還是做得窄一點(相對於新出的大屏幕),哪個選擇更好呢?
當智能手機出現的時候,開發人員終於要(被迫)要停止假裝每個人都可以在他們的網站上獲得相同的體驗了。不管我們喜不喜歡,我們都得放棄已知的多欄定寬(px)布局,並開始考慮響應式設計。我們再也不能逃避CSS所帶來的抽象概念(abstraction),相反,我們要去擁抱這項特性。
響應式 —— 在CSS中,這指的針對不同大小的瀏覽器窗口,用不同的方式響應更新頁面的樣式。我們要對不同尺寸的手機、平板電腦或桌面顯示器多花心思了。我們將在第8章中詳細介紹響應式設計,但在本章中,我會先給大家介紹一些重要的基礎概念。
增加的抽象概念意味著額外的複雜性。如果我設定一個寬度為800px的元素,那麼它在一個更小的窗口中會怎麼顯示呢?如果一個橫向菜單不能全部在一行展示完,它又會怎麼展示?在編寫CSS時,你需要能夠同時考慮具體情況以及普適性的問題。如果針對一個特定的問題,你有多種方式可以解決,那麼你應該選那個在多種不同場景下更通用的解決方案。
在抽象概念這個問題上,相對單位是CSS提供的工具之一。與其把字型大小大小設置為14px,你可以把它設置為與窗口大小成比例縮放。或者,你可以設置頁面上所有元素是依賴基礎字型大小大小的變化而變化的,然後用一行代碼就可以達到調整整個頁面的目的。接下來,我們來看看CSS提供了哪些方式來實現以上的效果。
像素(pixel)、點(point)和pc(pica)
CSS支持一些絕對長度單位,其中最常見也最基本的是像素(px)。較不常見的絕對單位有毫米(mm,millimeter)、厘米(cm,centimeter)、英寸(in.,inch)、點(pt,point,印刷術語,長度為1/72 inch)以及pc(pica,印刷術語,長度為12 points)。如果你想了解其中的計算方式,以上的長度單位都可以直接轉換成另一個單位:1 inch = 25.4 mm = 2.54 cm = 6 pc = 72 pt = 96 px。因此,16px與12pt(16/96×72)是等價的。設計師通常更熟悉點(point)的使用,而開發人員更習慣於像素,因此在和設計師溝通時,你可能需要在兩者之間做一些計算工作。
像素這個名字有點誤導性 —— 1 CSS像素並不嚴格等同於顯示器的1像素,在高解析度顯示器(如「Retina顯示屏」)上尤其明顯。儘管根據瀏覽器、操作系統和硬體的不同,CSS的測量值可能會有細微的差別,但96px總是會大致等於屏幕上的物理1英寸。(儘管有可能會因某些設備或用戶設置而異。)
2.2 em和rem
em是最常見的相對長度單位,這是排版中使用的一種度量方式,基準值是當前元素的字型大小大小。 在CSS中,1em表示當前元素的字型大小大小,實際值取決於在哪個元素上應用。圖2.1展示了一個padding為1em的div
。
[ 圖 2.1:padding為1em的元素(添加虛線是為了讓padding更明顯)]
模板代碼片段如下。這套樣式規則定義字型大小為16px,也就是元素本身1em代表的值,然後再使用em來聲明元素的padding。 把這段代碼添加到一個新的樣式表裡,在<div class ="padded">
下隨手寫些文字,然後到瀏覽器看看效果吧。
代碼片段 2.1:在padding上使用em
.padded { font-size: 16px; padding: 1em; 1}
- 1 把各個方向的padding的值設置為字型大小大小
把padding
賦值為1em,乘以字型大小,得到一個值為16px的padding
渲染值。重點來了,使用相對單位聲明的值會由瀏覽器轉化為一個絕對值,我們稱之為計算值。
在這個例子里,將padding
改為2em會生成一個32px的計算值。如果同一個元素的另一個選擇器,用一個不一樣的字型大小值去覆蓋它,這會改變em在這個域下的基準值,那麼padding
的計算值也會相應變化。
在設置padding、height、width或border-radius
等屬性時,使用em可能會很方便,因為如果它們繼承了不同的字型大小大小,或者用戶更改了字體設置,這些屬性會均勻地縮放。
圖2.2展示了兩個不同大小的盒子。盒子內的font-size
、padding
和border-radius
各不相同。
[ 圖 2.2:有相對大小的padding和border-radius的元素 ]
你可以通過用em聲明padding
和border-radius
來給這些盒子聲明樣式規則。首先給每個元素設定padding
和border-radius
為1em,然後給每個盒子指定不同的字型大小,那麼其他屬性會跟著字型大小縮放。
在你的HTML代碼里,創建如下的兩個盒子,類名分別是box-small
和box-large
,代表兩個字型大小修飾符。
[ 代碼片段 2.2:在不同元素上使用em(HTML)]
<span class="box box-small">Small</span><span class="box box-large">Large</span>
現在,添加下面的樣式到你的樣式表。這裡使用了em聲明了一個盒子。還定義了小字型大小和大字型大小的修飾符,指定不同的字型大小大小。
[ 代碼片段 2.3:在不同元素上使用em(CSS)]
.box { padding: 1em; border-radius: 1em; background-color: lightgray;}.box-small { font-size: 12px; 1}.box-large { font-size: 18px; 1}
- 1 不同的字型大小大小,這會改變元素的em實際值的大小。
這是em一個強大的功能。你可以定義一個元素的字型大小大小,然後使用一句簡單的聲明,就可以通過改變字型大小大小從而控制整個元素大小縮放。你後面將會創建另外一個這樣的例子,不過首先,我們來討論一下em和字型大小大小。
2.2.1 對font-size使用em
當font-size
使用em作為單位時,它的表現會有點不一樣。我之前說過,em是以當前元素的字型大小大小作為基準值的。但是,如果你把一個元素的字型大小設為1.2em的時候,這是什麼意思呢?一個元素的字型大小大小是不能等於它自己的1.2倍的。相反,在font-size
上的em會先從繼承到的字型大小大小衍生出來。
舉個簡單的例子,見圖2.3。以下展示了一些不同字型大小大小的文字。在代碼片段2.4,你會用到em來實現。
[ 圖 2.3 以em為單位的兩種不同的字型大小大小 ]
在你的頁面添加以下代碼片段。第一行文字,在<body>
標籤裡面,它會按body的字型大小大小渲染。第二部分,口號(slogan),繼承父元素的字型大小大小。
[ 代碼片段 2.4 相對font-size
的模板 ]
<body> We love coffee <p class="slogan">We love coffee</p> 1</body>
- 1 slogan從繼承了字型大小大小。
代碼片段中,CSS代碼片段聲明了body
的字型大小大小。為了更加清晰,在這裡我用了px來聲明。下一步,你可以用em來放大slogan的字型大小大小。
[ 代碼片段 2.5:在font-size上使用em ]
body { font-size: 16px;}.slogan { 1 font-size: 1.2em; 1} 1
- 1 計算:這個元素繼承到字型大小,乘以1.2
slogan聲明的字型大小大小是1.2em,為了計算轉換成像素值,你需要引用繼承的字型大小16px,16 * 1.2 = 19.2,所以計算字型大小值是19.2px。
提示
如果你已經知道以px為單位的基礎字型大小大小,但希望把它改用em聲明,下面有個簡單的計算公式:目標em值 = 目標像素值 / 父元素(被繼承元素)像素值。舉個例子,如果你想要一個10px的字型大小大小,父元素的字型大小是12px,10 / 12 = 0.8333em。如果你想要16px的字型大小大小,父元素字型大小是12px,那麼 16 / 12 = 1.3333em。我們會在這章里多次用到這個計算公式。
有一點對你很有幫助,對於大多數瀏覽器,默認字型大小大小是16px。技術上,關鍵字medium會被計算轉換為16px。
當我們在一個元素內用em同時聲明font-size和其他屬性
你已經使用過em聲明font-size
了(基於一個繼承的字型大小大小值)。以及,你也曾經使用em聲明其他屬性,如padding
和border-radius
(基於當前元素的字型大小大小值)。當你針對同一個元素使用em聲明font-size
和其他屬性的時候,em會變得很神奇。此時瀏覽器必須先計算font-size
,然後基於這個值再去計算其他值。這些屬性聲明的時候使用的是相同的em值,但很可能它們會有不同的計算值。
在之前的例子里,我們計算到字型大小大小是19.2px(繼承的16px乘以1.2em)。圖2.4是相同的slogan元素,但有額外的1.2em padding
以及為了讓padding
大小更加明顯的灰色背景。可以看出,padding
比font-size
稍微大一些,儘管它倆聲明的時候em值是一樣的。
[ 圖2.4 一個font-size為1.2em以及padding為1.2em的元素 ]
現在的情況是,這個段落從body
繼承了16px的字型大小大小,通過計算得到值為19.2px的字型大小計算值。這意味著,19.2px是1em在當前域的基礎值,而這個值會被用作計算padding
的值。對應的CSS代碼在下面,更新你的樣式表並查看你的測試頁面吧。
[ 代碼片段 2.6 在font-size和padding上使用em ]
body { font-size: 16px;}.slogan { font-size: 1.2em; 1 padding: 1.2em; 2 background-color: #ccc;}
- 1 賦值為 19.2 px
- 2 賦值為 23.04 px
在這個例子里,padding
的聲明值為1.2em,乘以19.2px(當前元素的字型大小大小),計算出23.04px。我們可以看到,儘管font-size
和padding
聲明時em值是一樣的,但它們的計算值是不一樣的。
字型大小收縮問題
當你使用em聲明多層嵌套的元素字型大小時,會產生意想不到的效果。要弄清楚每個元素的實際值,首先你需要知道它繼承的父元素的字型大小大小,如果父元素的字型大小也是用em聲明的,那麼你需要知道它的父元素的字型大小大小,在dom樹往上查,以此類推。
當你使用em聲明列表的字型大小大小,列表嵌套了好幾層,效果就更明顯了。大多數web開發者會發現在他們的職業生涯裡面,圖2.5的列表嵌套形式有點眼熟。文字在逐步縮小!正是因為em帶來的這一類煩人的問題,開發者才對em避而遠之。
[ 圖 2.5 嵌套列表中的字型大小縮小現象 ]
當你多層嵌套列表,而每一層聲明的字型大小大小以em為單位,字型大小收縮現象就會發生。在代碼片段2.7和2.8的例子里,無序列表的字型大小是0.8em。這個選擇器對頁面上所有的ul有效,所以當一個列表從另外一個列表繼承到字型大小大小的時候,em就產生複合效果。
[ 代碼片段 2.7 在列表上使用em ]
body { font-size: 16px;}ul { font-size: .8em;}
[ 代碼片段 2.8 多層嵌套的列表 ]
<ul> <li>Top level <ul> 1 <li>Second level 1 <ul> 2 <li>Third level 2 <ul> 3 <li>Fourth level 3 <ul> <li>Fifth level</li> </ul> </li> </ul> </li> </ul> </li> </ul> </li></ul>
- 1 這個列表嵌套在第一個列表裡,繼承了它的字型大小大小
- 2 然後這個列表又嵌套在另一個列表裡,繼承了第二個列表的字型大小大小
- 3 ……以此類推
每一個列表的字型大小大小是0.8乘以父元素的字型大小大小。這代表第一個列表的字型大小大小是12.8px,嵌套的子列表字型大小大小是10.24px(12.8px * 0.8),第三層列表的是8.192px,如此類推。同樣地,如果你給字型大小大小的賦值大於1em,相反,文字的字型大小會一層層變大。我們想要的效果是像圖2.6一樣,在頂層聲明字型大小的大小,但下面嵌套層級的列表字型大小保持不變。
[ 圖 2.6 字型大小正常的多層嵌套列表 ]
其中一種實現的方式可以看看代碼片段2.9。第一個列表的字型大小大小還是0.8 em(見示例2.7),第二個選擇器對嵌套在無序列表的無序列表有效 —— 也就是除了第一個無序列表以外的所有無序列表。現在嵌套的列表設定了跟父元素一致的字型大小大小,正如圖2.6一樣。
[ 代碼片段 2.9 字型大小收縮現象的糾正 ]
ul { font-size: .8em;}ul ul { 1 font-size: 1em; 1}
- 1 嵌套在列表裡面的列表,應該把字型大小大小設定為跟父元素一樣
這可以解決問題,但不是最優方案。因為你設置了一個字型大小值,馬上用另一個選擇器重寫了這個規則。如果你可以使用針對嵌套的列表聲明一個特定的選擇器,避免互相覆蓋,會是一個更好的方案。
到現在我們清楚了,如果你不是一個比較小心的人,你應該遠離em。使用em作為padding
、margin
和元素縮放效果的單位挺好的,但當em遇上font-size
時,事情可以變得很複雜。感謝上天,我們有個更好的選擇 —— rem。
(未完待續,請期待下一篇《如何更愉快地使用rem》)
《別說你懂CSS相對單位》系列譯文:
- 如何更愉快地使用em [本文]
- 如何更愉快地使用rem
- 視口相關單位的應用
- 無單位數字和行高
- CSS自定義屬性
章節:
- 2.1 相對單位值的魔力
- 2.1.1 完美像素設計(pixel-perfect design)的掙扎
- 2.1.2 完美像素網頁的終結
- 像素(pixel)、點(point)和pc(pica)
- 2.2 em和rem
- 2.2.1 對font-size使用em
- 當我們在一個元素內用em同時聲明font-size和其他屬性
- 字型大小收縮問題
- 2.2.2 對font-size使用rem
- 可用性:對font-size使用相對長度單位
- 2.3 停止使用像素思維去思考
- 2.3.1 設置一個合理的字型大小默認值
- 2.3.2 讓這個面板變得「響應式」
- 2.3.3 調整單個組件的大小
- 2.4 視口相關單位(viewport-relative units)
- CSS3
- 2.4.1 在font-size上使用vw
- 2.4.2 在font-size上使用calc()
- 2.5 不帶單位的數字(unitless number)和行高(line-height)
- 2.6 自定義屬性(也叫「CSS變數」)
- 2.6.1 動態改變自定義屬性的值
- 2.6.2 通過JavaScript改變自定義屬性的值
- 2.6.3 初探自定義屬性
- 總結
原著版權信息:
作者:Keith J.Grant書籍:CSS in Depth章節:Working with relative units
筆者 @Yuying Wu,前端愛好者 / 鼓勵師 / 紐西蘭打工度假 / 鏟屎官。目前就職於某大型電商的B2B前端團隊。
感謝你讀到這裡,對上文若有任何疑問或建議,歡迎留言。
如果你和我一樣喜歡前端,喜歡搗騰獨立博客或者前沿技術,或者有什麼職業疑問,歡迎關注我以及各種交流哈。
獨立博客:wuyuying.com
知乎ID:@Yuying Wu
Github:Yuying Wu推薦閱讀:
※前端日刊-2017.12.15
※跨域
※react和vue框架應該如何選擇?
※帶你走進webpack世界,成為webpack頭號玩家。