理解 line-height 和 vertical-align

文章 GitHub 地址:https://github.com/afishhhhh/blog/issues/4
文章若有錯誤,請各位可以指出。css

幾個概念

  1. line box:包裹 inline box 的一個盒子,一個或多個 line box 堆疊撐起一個 HTML 元素。
  2. inline(-level) box:能夠是一個由行內元素包裹的盒子,也能夠是一個純文字的匿名盒子。
  3. content area:對於非替換元素來講,content area 的範圍由 font-size 以及字體自己決定;對於替換元素來講,由元素自有寬高決定。
  4. baseline:一個元素基線的位置由該元素內字母 x 底部所在的位置決定,固然字體不一樣基線所在的位置也就不一樣。

經過一段代碼能夠理解一下:html

div {
  background-color: #ccc;
  font-size: 20px;
  color: #fff;
}
span {
  color: red;
}
<div>文字1<span>文字2</span>文字3</div>

50416531-27d82180-085c-11e9-91bf-7b06ee175b8d.png

白色的文字就是一個匿名 inline box,紅色的文字是一個由 span 包裹的 inline box。這三個 inline box 組成一個 line box,能夠理解爲灰色的區域,由於在這個例子裏就是由一個 line box 撐開了 div。若是有多行的文字,那就有多個 line boxgit

關於 content areaW3C 有一段這樣的解釋:github

CSS 2.1 does not define what the content area of an inline box is (see 10.6.1 above) and thus different UAs may draw the backgrounds and borders in different places.

這篇文章對非替換元素 content area 的定義就是自有寬高加上 margin,padding 以及 border。我認爲應該將 content area 理解爲 content boxweb

line box 高度

  1. 瀏覽器會計算 line box 中每個 inline box 的高度,對於不一樣的 inline box 計算方式有所不一樣:瀏覽器

    • 若是是一個替換元素(好比 imginput),inline-* 元素或者是 flexbox 中的子元素,高度由其 margin box 決定;wordpress

      inline-block 元素:字體

      div {
        background-color: #ccc;
        color: #fff;
      }
      span {
        display: inline-block;
        height: 30px;
        margin: 10px;
        background: #fff;
        color: red;
      }
      <div>xxx<span>xxx</span>xxx</div>

      50419864-23b4ff80-086e-11e9-968a-4d7f90fd1825.png

      這裏 span inline box 的高度就是 height + margin 2。若是 height 的值是 auto,高度就是等於 line-height + margin 2flex

    • 若是是一個非替換元素,高度由它的 line-height 決定,而不是 content area,雖然有時候看起來像 content area 撐開了 line box 的高度。flexbox

      div {
        background-color: #ccc;
        font-size: 20px;
        color: #fff;
        font-family: Sana;
      }
      span {
        background: #fff;
        color: red;
      }
      <div>xxx<span>xxx</span>xxx</div>

      50418229-3e36ab00-0865-11e9-8394-c26118b43d5e.png

      這張圖片能夠明顯地看出撐開 line box 的是 line-height,而不是 content area

      這篇文章用了 virtual-area height 來表示 line-height 撐開的高度,而個人理解其實就是 inline box 的高度。

  2. line box 中全部 inline box 的最高點以及最低點決定了它的高度(該計算包括了 strut 的高度,後文會提到 strut)。
  3. 非替換元素的的 marginpadding 以及 border 並不會影響 line box 高度的計算。當一個 inline-level boxline-height 小於 content area 的時候,line box 的高度就會小於 content area,此時元素的 background 以及 padding 等就會溢出到 line box 以外。

    如下代碼能夠說明這個問題:

    div {
        background: #eee;
        border: 1px solid #000;
        box-sizing: border-box;
        font-size: 50px;
        line-height: 10px;
    }
    span {
        background: red;
        margin: 10px;
        padding: 10px;
    }
    <div><span>xxx</span></div>

    50210206-801ca880-03b0-11e9-9795-2e8dfdeb3a84.png

leading:

content area 的高度與 inline box 的高度差就是 leading,這個 leading 會等分被添加到 content area 的頂部與底部,因此說 content area 永遠位於 inline box 的中間(垂直居中)

strut:

瀏覽器認爲每個 line box 的起始位置都存在一個寬度爲 0,沒有任何字符的 匿名 inline box,稱爲 strut,這個 strut 是會從父元素繼承 line-height 的,所以它的高度會影響整個 line box 高度的計算。

一個例子

50262533-c4f61d00-044c-11e9-85df-5cc70f2aced9.png

div { background: #eee; border: 1px solid #000; box-sizing: border-box; }
<div><img src="./image.png" alt=""></div>

在圖片中能夠看到 img 與外層的 div 存在一個間隙,這就是上文提到的 strut 形成的。

在這個例子中,默認狀況下 img 的底邊與父元素的基線對齊(img { vertical-align: baseline }),而這個基線實際上就是 strut 基線所在的位置。以下圖所示:

50263805-a2670280-0452-11e9-9990-531c203a0295.png
strut 其實就至關於一個不可見的字母 x,上文已經提到 strut 自己是具備 line-height 的,因此就致使圖片底部多了一段間隙。

總結一下存在間隙緣由:

  1. strut 存在 line-height
  2. vertical-align 默認值爲 baseline

對應的解決方案:

  1. 修改 strutline-height,由於 strutline-height 不是可以直接設置的,因此須要設置父元素的 line-height,而後讓 strut 繼承,或者修改 font-size
  2. vertical-align 設置爲其餘值

line-height

W3C 中對於 line-height 的解釋是這樣的:

On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut."

個人簡單理解是,對於由行內元素組成的塊級元素而言,line-height 決定了 line box 的最小高度,瀏覽器會假定每個 line box 以一個寬度爲 0 的 inline boxstrut)開始,而這個 strut 從父元素繼承到 font 以及 line-height

  • normalline-height 的默認值,W3C 對它並無一個明確的定義。normal 會將 content area 做爲一個計算因素。
  • line-height 並非兩條 baseline 之間的距離。
  • line-height 的值推薦使用數值,而不是使用 em 單位,由於 em 單位會根據從父元素繼承到的 font-size 來計算行高。

vertical-align

W3Cbaseline 以及 middle 的定義以下:

baseline: Align the baseline of the box with the baseline of the parent box. If the box does not have a baseline, align the bottom margin edge with the parent's baseline.

元素基線與父元素基線對齊,若是元素沒有基線,好比 img,則使用 margin 底邊與父元素基線對齊。

middle: Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.

元素的垂直中點位置與父元素的基線加上一半 x-height 的位置對齊。

參考

  1. Deep dive CSS: font metrics, line-height and vertical-align
  2. https://meyerweb.com/eric/css/inline-format.html
  3. https://www.zhangxinxu.com/wordpress/2015/08/css-deep-understand-vertical-align-and-line-height/
  4. https://www.w3.org/TR/CSS2/visudet.html#inline-box-height
相關文章
相關標籤/搜索