主要摘自:《CSS 揭祕》,強烈推薦的一本書。
「44 年前咱們就把人類送上月球了,但如今咱們仍然沒法在 CSS 中 實現垂直居中。」——James Anderson(https://twitter.com/jsa/ status/358603820516917249)css
在 CSS 中對元素進行水平居中是很是簡單的:若是它是一個行內元素, 就對它的父元素應用 text-align: center;
若是它是一個塊級元素,就對它自身應用 margin: auto
。然而若是要對一個元素進行垂直居中,可能光是想一想就使人頭皮發麻了。html
長久以來,爲了解決這一絕世難題,前端開發者們殫精竭慮,琢磨出了各類解決方法,惋惜大多數並不實用。在本篇攻略中,咱們將探索現代 CSS 的強大威力,以全新的思路去攻克各類場景下的垂直居中難題。請注意,有幾種技巧十分流行,但在這裏並不會深刻探討,緣由以下:前端
若是你有興趣,能夠去看看 Chris Coyier 寫的 Centering in the Unknown。這篇出色的文章詳細講述了這兩種技巧。瀏覽器
咱們將使用以下所示的結構代碼,並直接插入 <body> 元素中(但實際上咱們將要探索的這些技巧是與容器無關的):函數
<main> <h1>Am I centered yet?</h1> <p>Center me, please!</p> </main>
咱們先來看一個早期的垂直居中方法,它要求元素具備固定的寬度和高度:佈局
main { position: absolute; top: 50%; left: 50%; margin-top: -3em; /* 6/2 = 3 */ margin-left: -9em; /* 18/2 = 9 */ width: 18em; height: 6em; }
這段代碼在本質上作了這樣幾件事情:flex
藉助強大的 calc()
函數,這段代碼還能夠省掉兩行聲明:flexbox
main { position: absolute; top: calc(50% - 3em); left: calc(50% - 9em); width: 18em; height: 6em; }
顯然,這個方法最大的侷限在於它要求元素的寬高是固定的。spa
在一般狀況下,對那些須要居中的元素來講,其尺寸每每是由其內容來決定的。若是 能找到一個屬性的百分比值以元素自身的寬高做爲解析基準,那咱們的難題就迎刃而解了!遺憾的是,對於絕大多數 CSS 屬性(包括 margin)來講, 百分比都是以其父元素的尺寸爲基準進行解析的。設計
CSS 領域有一個很常見的現象,真正的解決方案每每來自於咱們最意想不到的地方。
在這個例子中,答案來自於 CSS Transforms 。當咱們在 translate()
變形函數中使用百分比值時,是以這個元素自身的寬度和高度爲基準進行換算和移動的,而這正是咱們所須要的。接下來,只要換用基於百分比的 CSS 變形來對元素進行偏移,就不須要在偏移量中把元素的尺寸寫死。這樣咱們就能夠完全解除對固定尺寸的依賴:
main { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
能夠看到,這個容器已經完美居中了,徹底知足咱們的指望。
固然,沒有任何技巧是十全十美的,上面這個方法也有一些須要注意的地方:
transform- style: preserve-3d
來修復,不過這個修復手段也能夠認爲是一個 hack,並且很難保證它在將來不會出問題。
線上例子:
http://dabblet.com/gist/cd12fac0e18bb27fb62d
假設咱們不想使用絕對定位,仍然能夠採用 translate()
技巧來把這 個元素以其自身寬高的一半爲距離進行移動;可是在缺乏 left 和 top 的狀況下,如何把這個元素的左上角放置在容器的正中心呢?
咱們的第一反應極可能是用 margin 屬性的百分比值來實現,就像這樣:
main { width: 18em; padding: 1em 1.5em; margin: 50% auto 0; transform: translateY(-50%); }
不過,如圖所示,這段代碼會產生十分離譜的結果:
緣由在於 margin 的百分比值是以父元素的寬度做爲解析基準的 。沒錯,即便對於 margin-top
和 margin-bottom
來講也是這樣!
不過幸運的是,若是隻是想把元素相對於視口進行居中,仍然是有但願的。CSS Values and Units Module Level 3 定義了一套新的單位,稱爲 viewport 相關的長度單位。
在咱們的這個例子中,適用於外邊距的是 vh 單位:
main { width: 18em; padding: 1em 1.5em; margin: 50vh auto 0; transform: translateY(-50%); }
能夠看到,其效果堪稱完美。固然,這個技巧的實用性是至關有限的,由於它只適用於在視口中居中的場景。
線上例子:
http://dabblet.com/gist/bf12b39d8f5da2b6e5b6
這是毋庸置疑的最佳解決方案,由於 Flexbox 是專門針對這類需求所設計的。咱們之因此要討論其餘方案,僅僅是由於那些方案在瀏覽器的支持程度上稍微好一些而已。但其實目前現代瀏覽器對 Flexbox 的支持度已經至關不錯了。
咱們只需寫兩行聲明便可:
display: flex
(在這個例子中是 <body>
元素)margin: auto
(在這個例子中是 <main>
元素):body { display: flex; min-height: 100vh; margin: 0; } main { margin: auto; }
請注意,當咱們使用 Flexbox 時,margin: auto 不只在水平方向上將元素居中,垂直方向上也是如此。還有一點,咱們甚至不須要指定任何寬度(固然,若是須要的話,也是能夠指定的):這個居中元素分配到的寬度等於 max-content
。
若是瀏覽器不支持 Flexbox,頁面渲染結果看起來就跟咱們的起點圖是同樣的了(若是設置了寬度的話)。雖然沒有垂直居中效果,但也是徹底能夠接受的。
Flexbox 的另外一個好處在於,它還能夠將匿名容器(即沒有被標籤包裹的文本節點)垂直居中。舉個例子,假設咱們的結構代碼是:
<main>Center me, please!</main>
咱們先給這個 main 元素指定一個固定的尺寸,而後藉助 Flexbox 規範 所引入的 align-items
和 justify-content
屬性,咱們可讓它內部的文本也實現居中:
main { display: flex; align-items: center; justify-content: center; width: 18em; height: 10em; }
線上例子:
http://play.csssecrets.io/vertical-centering-vh
根據 CSS Box Alignment Module Level 3 的計劃,在將來,對於簡單的垂直居中需求, 咱們徹底不須要動用特殊的佈局模式了。由於只須要下面這行代碼就能夠搞定:
align-self: center;
無論這個元素上還應用了其餘什麼屬性,這樣寫就夠了。這聽起來可能難以置信,但或許你電腦上的瀏覽器很快就會讓它成爲現實。