深入理解CSS:line-height、vertical-align(轉載)

轉至: https://www.cnblogs.com/wfeicherish/p/8884903.html

說在前面:通過這次深入學習CSS的line-height和vertical-align屬性,對CSS真是刮目相看,決心開始深入CSS學習哈哈。

一、從一個常見的需求開始

在一行中,左側圖標,圖標右側是文字,並且圖標和文字在這行垂直居中。

上代碼:

<div class="bg">
    <img src="hello.jpg"/>
    <span class="span1">abgxxx</span>
    <span class="span2">abgxxx</span>
</div>

在這裏插入圖片描述
  
  圖片看着很正常,內聯元素按順序在一行排列,但是當給外層div加上背景色和邊框之後就發現問題了。

.bg {
    background-color: lightblue;
    border: black solid 1px;
}
  

圖片下方出現了空白,這是由於img元素的vertical-align屬性默認爲baseline,baseline意味着元素的基線通父元素的基線對齊,父元素的基線爲字母x的下邊緣(線),但是像圖片或者輸入框這種元素,本身沒有基線,則是將其底端通父元素的基線對齊。
在這裏插入圖片描述
  那麼圖片下方的空白處的高度是怎麼確定的呢?

.bg span{
    background-color: lightgreen;
}

在這裏插入圖片描述

給後面的span加上背景色後,看出空白的高度即字符的基線baseline和bottom之間的距離,而這個距離是由line-height決定的,而line-height的默認值是normal,那normal 是什麼呢?我們經常將 normal 理解爲 1,或者 1.2,就連CSS規範中也沒有講明白normal究竟代表多少。我們知道 line-height 的值爲數字時,表示的相對於 font-size 的倍數,但問題在於,font-size:100px 對應的文字在不同字體裏的高度是不一樣的!那麼 line-height 會隨着文字大小的改變而改變嗎? normal 真的表示 1 或者 1.2 嗎?vertical-align 又是如何被 line-height 影響的呢?

讓我們來深入理解一個不那麼簡單的 CSS 機制。(下文中字體度量摘自方老師的譯文,見參考資料1)

二、font-size和字符度量

下面是一段簡單的 HTML 代碼,一個 p 標籤包含了 3 個 span 標籤,每個 span 各自有一個 font-family:

<p>
    <span class="a">Ba</span>
    <span class="b">Ba</span>
    <span class="c">Ba</span>
</p>  
p  { font-size: 100px }
.a { font-family: Helvetica }
.b { font-family: Gruppo    }
.c { font-family: Catamaran }

(注:這幾款字體我們的電腦上可能沒有,在我們自己電腦上可以設置三個字體查看區別:monospace、默認字體Microsoft YaHei、fantasy,font-size均設置爲40px,見下方)

<div class="bg" style="background-color:  lightblue;border: black solid 1px;">    
    <img src="hello.jpg">
    <span class="span1" style="font-family: monospace;background-color: lightgreen;font-size: 40px;">abgxxx</span>
    <span style="font-size:40px;">abgxxx</span>
    <span style="font-family: fantasy;background: lightcoral;font-size: 40px;">abgxxx</span>
</div>

在這裏插入圖片描述
在這裏插入圖片描述
  font-size 相同,font-family 不同,得到的 span 元素的高度也不同!

爲什麼 font-size: 100px 不能得到相同高度的元素呢?測量了一下每個 span 的高度:Helvetica 115px,Gruppo 97px,Catamaran 164px。
  在這裏插入圖片描述

乍看很奇怪,但是仔細想想,這麼做又是很有道理的。原因在於字體本身,這是字體的原理:

  • 一款字體會定義一個 em-square,它是用來盛放字符的金屬容器。這個 em-square 一般被設定爲寬高均爲 1000 相對單位,不過也可以是 1024、2048 相對單位。
    在這裏插入圖片描述
  • 字體度量都是基於這個相對單位設置的,包括 ascender、descender、capital height、x-height 、linegap等。注意這裏面的值是允許相對於 em-square 出血(bleed outside)的,可以理解爲超出 em-square
      來幾張圖:
    在這裏插入圖片描述  
    在這裏插入圖片描述
     在這裏插入圖片描述

(注:後面兩張標註的字體度量值中的leading不是很明確,原諒我實在沒找到合適的圖描述linegap即leading的,看到下文vertical-align:normal的取值過程,你就能明白了!請耐心,繼續往下看哦)

  • 在瀏覽器中,上面的 1000 相對單位會按照設定的font-size 縮放。
    我們把 Catamaran 字體放到 FontForge 中,分析它的字體度量:
  • em-square 是 1000
  • ascender 是 1100,descender 是 540。通過測試發現,macOS 上的瀏覽器使用了 HHead Ascent 和 HHead Descent 值,Windows 上的瀏覽器使用了 Win Ascent 和 Win Descent(而且兩個平臺上的值不一樣)。我們還看到 Capital Height 是 680,X height 是 485。
      在這裏插入圖片描述

這意味着 Catamaran 字體佔據了 1100 + 540 個相對單位,儘管它的 em-square 只有 1000 個相對單位,所以當我們設置 font-size:100px 時,這個字體裏的文字高度是 164px。這個計算出來的高度決定了 HTML 元素的 content-area(內容區)。

我們還能看出大寫字母的高度是 68px,小寫字母的高度(x-height)是 49px。所以 1ex = 49px,1em = 100px,而不是 164px。(em 是基於 font-size,而不是基於計算出來的高度)
在這裏插入圖片描述

當 p 元素出現在屏幕上時,它可能包含了多行內容,每行內容由多個內聯元素組成(內聯標籤或者是包含文本的匿名內聯元素),每一行都叫做一個 line-box。line-box 的高度是由它所有子元素inline-box的高度計算得出的。瀏覽器會計算這一行裏每個子元素的高度,再得出 line-box 的高度(具體來說就是從子元素的最高點到最低點的高度),所以默認情況下,一個 line-box 總是有足夠的高度來容納它的子元素。

每個 HTML 元素實際上都是由多個 line-box 的容器,如果你知道每個 line-box 的高度,那麼你就知道了整個元素的高度。

如果我們修改一下最初的 HTML 代碼:

<p>
    Good design will be better.
    <span class="a">Ba</span>
    <span class="b">Ba</span>
    <span class="c">Ba</span>
    We get to make a consequence.
</p>

那麼就會得到 3 個 line-box(寬度固定):

  • 第一行和最後一行各有一個匿名內聯元素(文本內容)
  • 中間一行包含兩個匿名內聯元素和三個 span

在這裏插入圖片描述

我們清楚地看到第二個 line-box 比其他兩個要高一些。因爲第二行裏面的子元素因爲有一個用到了 Catamaran 字體的 span。

line-box 的難點在於我們看不見它,而且不能用 CSS 控制它。即使我們用 ::first-line 給第一行加上背景色,我們也看不出第一個 line-box 的高度。

三、line-height

上文中已經提到過content-area,它的高度是由字體度量決定的,而line-box 的高度是根據子元素的高度計算出來的。

對於一個內聯元素,它有兩個高度:content-area(內容區高度)和vitual-area(實際高度),實際高度就是 line-height,這個高度用於計算 line-box 的高度

line-height 並非表示兩個 baseline 之間的距離。

在這裏插入圖片描述
 在這裏插入圖片描述

virtual-area 和 content-area 高度的差異叫做 leading。leading 的一半會被加到 content-area 頂部,另一半會被加到底部。因此 content-area 總是處於 virtual-area 的中間。

計算出來的 line-height(也就是 virtual-area 的高度)可以等於、大於或小於 content-area。如果 virtual-area 小於 content-area,那麼 leading 就是負的,因此 line-box 看起來就比內容還矮了。

還有一些其他種類的內聯元素:

  • 可替換的內聯元素,如 img / input / svg 等
  • inline-block 元素,以及所有 display 值以 inline- 開頭的元素,如 inline-table /
    inline-flex
  • 處於某種特殊格式化上下文的內聯元素,例如 flexbox 元素中的子元素都處於flex formatting
    context(彈性格式化上下文)中,這些子元素的 display 值都是「blockified」

這類內聯元素,其高度是基於 height、margin、padding 和 border 屬性。如果將 height 設置爲 auto的話,那麼其高度的取值就是 line-height,其 content-area 的取值也是 line-height。

在這裏插入圖片描述

下面解釋 line-height:normal 是什麼意思

回到 FontForge,Catamaran 的 em-square 高度是 1000,同時我們還看到很多其他的 ascender/descender 值:
在這裏插入圖片描述

  • 常規的 Ascent/Descent:ascender 是 770,descender 是 230,用於渲染字符。
  • 規格 Ascent/Descent:ascender 是 1100,descender 是 540。用於計算 content-area
    的高度
  • 規格 Line Gap:用於計算 line-height: normal。

在 Catamaran 這款字體中,Line Gap 的值是 0,那麼 line-height: normal 的結果就跟
content-area 的高度一樣,是 1640 相對單位。

爲了對比,我們再看看 Arial 字體,它的 em-square 是 2048,ascender 是 1854,descender 是 434,line gap 是 67。那麼當 font-size: 100px 時,

  • 其 content-area 的高度就是 100/2048*(1854+434) = 111.72,約爲 112px;
  • 其 line-height: normal 的結果就是 100/2048*(67+1854+434) 約爲 115px。

所有這些值都是由字體設計師設置的。

這麼看來,line-height:1 就是一個很糟糕的實踐。當 line-height 的值是一個數字時,其實就是相對 font-size 的倍數,而不是相對於 content-area。所以 line-height:1 很有可能使得 virtual-area 比 content-area 矮,從而引發很多其他的問題。
  在這裏插入圖片描述
  line-box 計算的一些細節:

  • 對於內聯元素,padding 和 border 會增大 background 區域,但是不會增大 content-area(不是 line-box 的高度)。一般來說你無法再屏幕上看到 content-area。margin-top 和 margin-bottom對兩者都沒有影響。
  • 對於可替換內聯元素(replaced inline elements)、inline-block 元素和 blockified 內聯元素,padding、margin 和 border 會增大 height(譯者注:注意 margin),因此會影響 content-area 和 line-box 的高度

四、vertical-align

vertical-align 屬性,它也是計算 line-box 高度的重要因素之一。

它的默認值是 baseline。還記得字體度量裏的 ascender 和 descender 嗎?這兩個值決定了 baseline 的位置。很少有字體的 ascender 和 descender 的比例是一比一的,所以我們經常看到一些意想不到的現象,下面是例子。

代碼如下:

    <p>
        <span>Ba</span>
        <span>Ba</span>
    </p>
    p {
        font-family: Catamaran;
        font-size: 100px;
        line-height: 200px;
    }

一個 p 標籤內有兩個 span 標籤,span 繼承了 font-family、font-size 和 200px 的 line-height。這時兩個 span 的 baseline 是等高的,line-box 的高度就是 span 的 line-height。

在這裏插入圖片描述

如果第二個 span 的 font-size 變小了

span:last-child {
    font-size: 50px;
}

我們會發現一個非常line-box 的高度變高了!如下圖所示。提示你一下,line-box 的高度是從子元素的最高點到最低點的舉例。
在這裏插入圖片描述

我們來看另一個例子。p 標籤有 line-height:200px,內含一個 span,span 繼承了 p 的 line-height。

  <p>
        <span>Ba</span>
    </p>
p {
    line-height: 200px;
}
span {
    font-family: Catamaran;
    font-size: 100px;
}

此時 line-box 的高度是多少?貌似是 200px,但其實不是。這裏需要考慮到的問題是 p 有自己的 font-family,默認值是 serif。p 的 baseline 和 span 的 baseline 位置不一樣,因此最終的 line-box 比我們預想的要高一些。出現這種問題是因爲瀏覽器認爲每個 line-box 的起始位置都有一個寬度爲 0 的字符(CSS 文檔將其稱爲 strut),並將其納入 line-box 的高度的計算中。

看不見的字符,看得見的影響。

爲了說明這個問題,我們畫圖解釋一下這個問題。
  在這裏插入圖片描述
  用 baseline 來對齊令人費解,如果我們用 vertical-align: middle 會不會好一點呢?CSS 文檔說明:middle 的意思是「用父元素 baseline 高度加上父元素中 x-height 的一半的高度來對齊當前元素的垂直方向的中點」。baseline 所處的高度跟字體有關,x-height 的高度也跟字體有關,所以 middle 對齊也不靠譜。更糟糕的是,一般來說,middle 根本就不是居中對齊!內聯元素的對齊受太多因素影響,因此不可能用 CSS 實現。

順便一說,vertical-align 的其他 4 個值有可能有點用:

  • vertical-align: top / bottom,表示與 line-box 的頂部或底部對齊
  • vertical-align: text-top / text-bottom,表示與 content-area 的頂部或底部對齊
    在這裏插入圖片描述

不過依然要小心,大部分情況下,對齊的是 virtual-area,也就是一個不可見的高度。看看下面這個用 vertical-align:top 的例子:
在這裏插入圖片描述

最後,vertical-align 的值也可以是數字,表示根據 baseline 升高或降低,不建議使用數字。

  • line-box 的高度的受其子元素的 line-height 和 vertical-align 的影響
  • 我們無法輕易的用 CSS 來控制字體度量

小結:

1、字體度量相關概念:

字體度量是指對於指定字號的某種字體,在度量方面的各種屬性,其描述的參數包括:

  • em-square: 相對單位
  • baseline: 字符基線
  • ascender: 字符最高點到baseline的距離
  • descender: 字符最低點到baseline的距離
  • linegap(leading): line-height爲normal時的字符實際高度與content-area的差
  • capitalHeight: 大寫字母頂部到baseline的距離

2、line-height:

語法   line-height : normal | <實數> | <長度> | <百分比> | inherit
說明 設置元素中行的高度  
   
  normal 默認行高,一般爲1.1.2,基於字體度量計算出來的,計算公式如上
  實數 實數值,縮放因子,相對於font-size的倍數,而不是content-area,可能得出一個比 virtual-area 還要矮的 content-area
  長度 合法的長度值,可以取負值
  百分比 百分比取值基於元素的字體尺寸
初始值 normal  
繼承性 繼承  
計算值 長度和百分比值爲絕對值,其他爲指定值  
  • 當內容中含有圖片時,如果圖片的高度大於行高,則含有圖片行的line box將被撐開到圖片的高度(雖然撐開了linebox,但不會影響line-height,因此也不會影響到基於行高來計算的其他屬性)
  • 所有的內聯元素都有兩個高度:基於字體度量的 content-area、virtual-area(也就是 line-height),這兩個高度都無法看到

3、vertical-align:

語法

vertical-align : baseline | sub | super | top | text-top | middle | bottom | text-bottom | <百分比> | <長度> | inherit
說明 設置元素內容的垂直對齊方式  
參數    
  baseline 基線對齊:使元素的基線同父元素的基線對齊,像如片圖片或者輸入框這類替換元素,本身沒有基線,則將其底端同父元素的基線對齊
  sub 下標
  super 上標:使元素的基線(替換元素的底端)相對於父元素的基線升高,移動的幅度CSS規範中沒有規定
  top 頂端對齊:將元素的行內框的頂端同行框的頂端對齊,即與line-box的頂部對齊
  text-top 與文本的頂端對齊:將元素的行內框的頂端同文本行的頂線對齊,即與content-area頂部對齊
  middle 中部對齊:通常使用在圖片上,將圖片的垂直方向的中線在基線位置上方半個ex的高度,注意ex值與字體尺寸有關
  bottom 底端對齊
  text-bottom 文本的底端對齊
  百分比和長度 CSS2,可爲負數
初始值 baseline  
繼承性 不繼承  
適用於 行內元素和單元格(table-cell)元素  
計算值 百分比和長度值爲絕對長度;其他同指定值  

line-box 計算:

  • 對於內聯元素,padding 和 border 會增大 background 區域,但是不會增大 content-area(不是line-box 的高度)。一般來說你無法再屏幕上看到 content-area。margin-top 和 margin-bottom對兩者都沒有影響
  • 對於可替換內聯元素(replaced inline elements)、inline-block 元素和 blockified內聯元素,padding、margin 和 border 會增大 height(譯者注:注意 margin),因此會影響
    content-area 和 line-box 的高度
  • 受其子元素的 line-height 和 vertical-align 的影響
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章