記一次解決字體圖標垂直對齊問題的過程

最近處理了一個字體圖標沒有在容器中垂直居中的樣式問題。本來覺得,只是 css 寫的不正確,實際卻並無那麼簡單。
一番波折後,最終發現,是由於一處小細節,挖出了個大坑。
在處理問題的整個過程當中,一方面複習了相關的 css 基礎知識;另外一方面,對於問題緣由的推理方法上,也給了我一些新的啓示,故特此記錄下來。css

原由

同事小 w 請教了我一個樣式問題:他寫的頁面上,一處本該垂直居中的字體圖標,變成了與容器頂部對齊,不知道是哪裏 css 寫的不對,
如圖所示(因爲事故現場已不存在,此處爲模擬還原的場景): html

示例-1
html 結構以下:

<div class='wrapper'>
  <div class='inner'>
    <i class="far fa-check-circle"></i>
  </div>
</div>
複製代碼

css 以下:瀏覽器

.outer {
  height: 60px;
  line-height: 60px;
  text-align: center;
  color: white;
  background-color: gray;
}

.inner {
  display: inline-block;
  font-size: 26px;
  line-height: 1;
}
複製代碼

分析

能夠看到,html 結構很簡單,分爲三部分,即外部容器.outer,內部容器.inner,以及字體圖標自己。
外部容器將heightline-height同設爲 60px,是經典的垂直居中的方法,然而,結果卻並無實現居中。
問題出在哪裏了呢?
那麼就先從垂直居中的原理來分析一下吧。markdown

利用 line-height 垂直居中

咱們常常說,「讓height等於line-height能夠實現垂直居中」。其實,這個說法自己,是有很多問題的。
首先,對於一個容器和一個內聯元素來講,並不須要同時設置heightline-height,只要給容器設置line-height,就能夠「垂直居中」了,以下圖: app

示例-2

<div class='wrapper'>
  <span class='text'>中文文本</span>
</div>
複製代碼
.wrapper {
  margin-bottom: 20px;
  line-height: 100px;
  color: white;
  background-color: gray;
  text-align: center;
}
複製代碼

不須要設height很好理解,由於line-height也能夠將容器高度撐開,此時的height的值爲auto,自動計算成了line-height的值。
之因此會有「讓height等於line-height」的說法,是由於最先這種作法是基於高度已經固定的容器,要使得其中的文本垂直居中,就須要設置容器的line-height等於高度。(因此也能夠直接去掉高度,改成設置line-height
但這裏還有個問題,其實文本並無真正的「垂直居中」,確切的說,是文本的「內容區域」居中了。若是給文本設置背景色,就能夠看出其內容區域: 字體

示例-3
幸運的是,對於咱們經常使用的字體,字體設計師在設置字體屬性(Font Metrics)時,會盡量的使字體處在內容區域的垂直居中位置,也所以,字體在容器中也是垂直居中的。
然而,這種幸運,對於 display: inline-block的元素來講,是不存在的。以下圖:
示例-4

<div class='wrapper'>
  <span class='cube'></span>
</div>
複製代碼
.wrapper {
  margin-bottom: 20px;
  line-height: 100px;
  color: white;
  background-color: gray;
  text-align: center;
}

.cube {
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: white;
}
複製代碼

設置 vertical-align

對於display: inline-block的元素,要使其垂直居中,咱們一般會在元素上添加vertical-align: middle使其垂直居中,如圖: ui

示例-5

.wrapper {
  margin-bottom: 20px;
  line-height: 100px;
  color: white;
  background-color: gray;
  text-align: center;
}

.cube {
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: white;
  vertical-align: middle;
}
複製代碼

這是什麼原理呢?
參考 w3c 對於vertical-align的定義,當設置爲middle時:spa

Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.翻譯

翻譯過來,就是說,vertical-align: middle的元素,會和父元素所用字體的 baseline 高度加上 x-height 的一半對齊,而這個值,其實就是小寫字母 x 的中線的高度,如圖: 設計

示例-6
也所以,能夠發現,使用這個方法實現的垂直居中並不可靠,它依賴於父元素的字體屬性。事實上,上圖中,元素的位置也並無絕對的垂直居中,緣由就是父元素使用的字體,其小寫 x 並無在內容區域垂直居中(偏下了)。
由此也能夠解釋,若是不設置 vertical-align,即 vertical-align: baseline時,爲何元素會偏高:
參見 vertical-align: 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.

因此,元素塊的底部,與 baseline 對齊,也就是字母 x 默認所處位置的那條線:

示例-7

嘗試修復

完成了以上的分析,再回頭看最開始的問題,發現,做爲內容器做爲display: inline-block元素,沒有添加vertical-align屬性,因而咱們加上它試試:

示例-8

.wrapper {
  height: 60px;
  line-height: 60px;
  text-align: center;
  color: white;
  background-color: gray;
}

.inner {
  display: inline-block;
  font-size: 26px;
  line-height: 1;
  vertical-align: middle;
}
複製代碼

發現並無用。難道是由於只有一個字體圖標,沒有一個參照物的緣由?(以前的中文字符不止一個字)
帶着這種設想,在字體圖標旁,添加一個字符試試,以下:

示例-9
果真,當添加了一個字符以後,字體圖標也能夠居中了。
難道,要使圖標居中,我必定要寫個隱藏字符在圖標旁邊才行嗎?爲何之前用相似方式,沒有遇到該問題呢?
因而,我在其餘頁面嘗試單個字符狀況下,是否能居中,結果以下:
示例-10
爲何其餘頁面,沒有這個問題呢?我再次翻開 w3c 官方定義文檔,在行高的章節中,找到這段描述:

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." (The name is inspired by TeX.).

以及在vertical-align段落中寫道:

The following values only have meaning with respect to a parent inline element, or to the strut of a parent block container element.

簡而言之,當只有一個字符時,瀏覽器會在文本前,設置一個 0 寬度的隱藏字符,做爲對齊的參考。因此說,並不須要去手寫一個字符來對齊。
這下,我凌亂了。。。

DOCTYPE 陷阱

冷靜一下後,繼續開始分析。發現,不一樣頁面上,html 結構和 css 是徹底一致的,但爲何實際樣式會不同呢?
再根據以上的理論分析,發現沒有居中的緣由,極可能是瀏覽器沒有提供這樣一個strut,做爲參考。而這,並不符合 w3c 定義的規範。
想到這裏,忽然意識到了什麼,在對比一下頁面,發現了惟一一處不一樣點:

示例-11
是的,頁面沒有設置 DOCTYPE!
當 DOCTYPE 沒有設置時,頁面會進入 Quirks Mode,即怪異模式。
怪異模式是爲了解決,css 規範出現後,保證遺留頁面依然可以正常顯示而制定的一種 HTML 文檔類型。正是因爲頁面處於怪異模式,才並無依照 w3c 規範的定義,使圖標垂直居中。
趕忙添加 <!DOCTYPE html>,這下問題終於解決了。

總結

首先,對於垂直居中問題,利用line-height未必能實現,這個須要根據具體狀況來分析,而分析的依據,正是 w3c 中的基本定義。因而可知,對於基礎概念,不該當只停留在會用的階段,仍是須要完整的閱讀一遍,理解透徹; 其次,對於排查問題,除告終合理論依據外,嘗試作對比是一個很好的方式。經過對比,一步步排除不相關緣由,最後就能挖出問題所在; 最後,在排查的過程當中,決不能無憑無據預設條件。由於在此次,糾結了好久的一個緣由,正是由於沒有考慮到,頁面的標準自己存在問題,而把時間花在尋找使用方式上是否是還存在未知錯誤。直到後來,一步步排除了以後,才鎖定到了最終緣由。

相關文章
相關標籤/搜索