|
本文在evernote里有備份。如果evernote的閱讀區(qū)域嫌窄了,那么可以把這個(gè)鏈接拖入書簽并點(diǎn)擊javascript:jQuery("#container").width(980);
本文從這個(gè)回答整理而來。對于當(dāng)今出現(xiàn)的一些CSS垂直居中的方案,這篇文章將會系統(tǒng)地審視它們,從實(shí)用角度進(jìn)行評估。
不羅嗦,先上圖:

考量需求及難度
為避免混淆,本文中所說的父容器和子容器都不是相對的,而對應(yīng)著如下文檔結(jié)構(gòu)里面的.outer和.inner容器:
<div class="outer"> <!-- 父容器 -->
<div class="inner"> <!-- 子容器 -->
... <!-- 子容器內(nèi)部內(nèi)容 -->
</div>
... <!-- 父容器內(nèi)其他的子元素 -->
</div>
我們從如下角度來評估:
- 父容器/子容器的高度是否可變
- 是否需要手工計(jì)算
- 子元素溢出時(shí)父元素是截?cái)唷⒈粨胃哌€是保留滾動條
- 當(dāng)然,還有兼容性
高度相關(guān)
| 代碼 |
效果 |
height:100px; |
定高 |
height:auto |
不定高,自適應(yīng)于自身的content-box |
height:50% |
不定高,自適應(yīng)于包含塊 |
出現(xiàn)了 3(定高/自適應(yīng)于外部/自適應(yīng)于內(nèi)部) ^ 2 (父容器/子容器) =9 種情形,搭配見下:
| 父容器 |
子容器 |
難度 |
解(tu)說(cao) |
| 定高 |
定高 |
簡單 |
快速開發(fā)時(shí)就是這樣 |
| 定高 |
自適應(yīng)于內(nèi)部 |
稍難 |
常見的需求 |
| 定高 |
自適應(yīng)于外部 |
簡單 |
手工算一算就好了 |
| 自適應(yīng)于內(nèi)部 |
定高 |
簡單 |
變相定高,高度放在內(nèi)層,方便解耦 |
| 自適應(yīng)于內(nèi)部 |
自適應(yīng)于內(nèi)部 |
稍難 |
留白固定,罕見的需求 |
| 自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
WTF |
折騰出這種需求,不覺得害臊嗎 |
| 自適應(yīng)于外部 |
定高 |
困難 |
適配所有屏幕的slideshow |
| 自適應(yīng)于外部 |
自適應(yīng)于內(nèi)部 |
困難 |
普適性更高的slideshow |
| 自適應(yīng)于外部 |
自適應(yīng)于外部 |
稍難 |
一個(gè)模塊(或大小不定的頭像),出現(xiàn)在大小不定的位置 |
溢出相關(guān)
子容器溢出代表子容器高度大于父容器高度的情形。
- 子容器溢出時(shí),被父容器截?cái)?。父容?code>overflow:hidden。
- 子容器溢出時(shí),把父容器撐高。父容器
height:auto;overflow:visible或display:table-cell等等。
- 子容器溢出時(shí),父容器出現(xiàn)滾動條。父容器
overflow:scroll或overflow:auto
很顯然,子容器溢出時(shí),被父容器截?cái)嗟那樾螣o法和父容器自適應(yīng)于子容器共存。
兼容性
- IE6、IE7:老而不死的瀏覽器,瀏覽器尚未統(tǒng)一、IE一家獨(dú)大之時(shí)的遺毒,一大堆bug等著你。
- IE8:IE8起全面支持CSS2.1,剩下一些稍微少坑爹那么一點(diǎn)點(diǎn)的bug。
- IE9。尷尬的產(chǎn)物,微軟第一次搭上CSS3的末班車。
- IE10+與其他現(xiàn)代瀏覽器(終于可以和其他瀏覽器并列了……IE10淚目)。
4、3、2、1的兼容難度是一步步變難,兼容到4、3、2、1所對應(yīng)的代碼量/工作量是100%、102%、120%、300%的關(guān)系。
在這里僅僅考量IE6/7,IE8的無bug實(shí)現(xiàn)的兼容性。IE9+的兼容性,對于文中提到的所有方案都是可行的。
其他
- 是否需要手動計(jì)算/需要calc屬性進(jìn)行輔助計(jì)算
如果需要手動計(jì)算,若界面進(jìn)行重構(gòu),而居中的需求不變,就需要重新計(jì)算。比較費(fèi)時(shí)費(fèi)事。布局也相對不夠靈活。
方案匯總
| CSS 版本 |
布局類型 |
方案 |
| CSS 2.1 |
普通流,塊級布局 |
父容器上下等padding |
| 子容器上下等margin |
| 普通流,行內(nèi)布局 |
父容器line-height=height |
| 子容器所在的line-box的line-height=父容器的height |
| 普通流,塊級table布局 |
父容器display:table-cell,子容器vertical-align |
| 子容器被table、tr、td包裹 |
| 絕對定位布局 |
子容器絕對定位,top:50%,負(fù)margin |
| 子容器絕對定位,top:0,bottom:0,margin:auto |
| 普通流,塊級布局 |
background代圖片background-position |
| CSS3 |
絕對定位布局 |
子容器絕對定位,top:50%,translateY(-50%) |
| 普通流,塊級布局 |
backgournd代圖片backgournd-size |
| 普通流,flexbox布局 |
flexbox |
方案評估
1. 普通流,塊級布局 (css2.1)
| 方案 |
兼容性 |
父容器 |
子容器 |
子容器溢出 |
其他 |
|
IE8 |
IE6/7 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
撐高 |
被截?cái)?/td>
| 不需手工計(jì)算 |
| 父容器上下等padding |
√ |
√ |
√ |
√* |
× |
√ |
√* |
× |
√ |
√* |
× |
| 子容器上下等margin |
√ |
√ |
√ |
√* |
× |
√ |
√* |
× |
√ |
× |
× |
| background代圖片background-position |
√ |
√ |
√ |
√ |
√ |
√ |
× |
× |
× |
√ |
√ |
1.1. 父容器上下等padding & 子容器上下等margin
最基礎(chǔ)的方案就是這樣,手工算好每一個(gè)容器的高度和補(bǔ)白/位移需要的內(nèi)容,簡單粗暴:
/*父子均定高,父容器上下等padding*/
.outer{ height: 100px; padding-top: 40px ; }
.inner{ height: 100px; }
/*父子均定高,子容器上下等margin*/
.outer{ height: 180px; overflow: hidden; *zoom: 1; }/*父容器給予BFC以避免子容器margin并到父容器上*/
.inner{ height: 100px; margin-top: 40px ; }
父子都需要自適應(yīng)于內(nèi)部時(shí):
/*父子均自適應(yīng)于內(nèi)部,父容器上下等padding*/
.outer{ height: auto; padding-top: 40px ; padding-bottom: 40px ; }
.inner{ height: auto; }
/*父子均自適應(yīng)于內(nèi)部,子容器上下等margin*/
.outer{ height: auto; overflow: hidden; *zoom: 1; }/*父容器給予BFC和haslayout,以避免子容器margin并到父容器上*/
.inner{ height: auto; margin-top: 40px ; margin-bottom: 40px ; }
說明:
- 需求:父子都需要自適應(yīng)于內(nèi)部的解決方案里,是把留白的高度固定,這種需求其實(shí)很少見。
可缺?。?code>height:auto可以省去,這是默認(rèn)值。
對于塊級元素,默認(rèn)是height:auto;width:auto;
width的auto值是自適應(yīng)于這個(gè)元素的包含塊的寬度,而height自適應(yīng)于這個(gè)元素的content-box的高度
自適應(yīng):父子容器均可以自適應(yīng)于內(nèi)部;也可以父容器自適應(yīng),子容器定高。
因?yàn)?code>height:auto時(shí),計(jì)算高度值的依賴方向是從外往內(nèi)。
自適應(yīng):為何在這倆方案里不容許出現(xiàn)父容器或子容器自適應(yīng)于外層呢?
因?yàn)閷τ趆eight來說,它的百分比值是乘以包含塊的height,但padding-top/padding-bottom/margin-top/margin-bottom的百分比值是乘以包含塊的width,而非我們希望的height。
溢出:
* 父容器上下等padding時(shí),父容器定高時(shí),子容器溢出padding-edge的部分會被截?cái)唷?br>
* 子容器上下等margin時(shí),父容器自適應(yīng)于內(nèi)部,父容器只會被撐高,永遠(yuǎn)無法被截?cái)唷?br>
* 其他情形,截?cái)喽紱]有什么意義
1.3. background代圖片background-position
在子容器是<img>標(biāo)簽時(shí),可以直接使用background來替代它。也可以算得上一種方案。
.inner { background-image: url("...");
background-position: 50% 50%;/*注,火狐不支持background-position-y的屬性設(shè)置*/ }
說明:
自適應(yīng):無法自適應(yīng)于外部容器,CSS2.1階段,background無法相對于容器伸縮。
溢出:背景溢出的情形,直接會被截?cái)唷?/p>
其他:你甚至可以把inner的標(biāo)簽省掉,直接把背景放到outer之上
2. 普通流,行內(nèi)布局 (css2.1)
| 方案 |
兼容性 |
父容器 |
子容器 |
子容器溢出 |
其他 |
|
IE8 |
IE6/7 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
撐高 |
被截?cái)?/td>
| 不需手工計(jì)算 |
| 父容器line-height=height |
√* |
× |
√ |
× |
× |
√ |
√ |
√ |
√ |
× |
√ |
| 子容器所在的line-box的line-height=父容器的height |
√* |
√* |
√ |
× |
√ |
√ |
√ |
√ |
√ |
× |
√ |
2.1 父容器line-height=height
“父容器line-height=height”,這個(gè)方案在于將子容器當(dāng)做行內(nèi)元素呈遞,并設(shè)置vertical-align,line-box和block-box的高度持平,就完成了垂直居中的的效果。
.outer { line-height: 100px; height: 100px; font-size: 0; }
.inner { display:inline-block; vertical-align:middle; font-size: 16px; }
為何需要font-size:0?因?yàn)関ertical-align:middle的定義是:元素的中垂點(diǎn)與父元素的基線加1/2父元素中字母x的高度對齊。因此在font-size>0時(shí),元素將會在baseline上出現(xiàn)一定的偏移,偏移量跟這個(gè)字號下的x字母的高度有關(guān)。
可以在jsFiddle中看到對比:
不使用font-size:0:
http:///humphry/haaaM/
使用font-size:0:
http:///humphry/7zCEm/
說明:
兼容性:沒有涉及relative和absolute定位,布局時(shí)相對無痛。
兼容性:這個(gè)方案是IE6\7不支持的,原因不詳(只知道inline-block和vertical-align的標(biāo)準(zhǔn)誕生于IE8之后),也沒有時(shí)間研究。2014年了,有點(diǎn)追求好嗎。
自適應(yīng):子容器可以輕松做到自適應(yīng)于內(nèi)部、外部或者定高,但在這個(gè)方案里,父容器必須定高,因?yàn)?code>line-height的百分比單位是相對font-size來說的。
溢出:子容器溢出時(shí)會變成頂對齊,這是因?yàn)椋琹ine-box 的高度跟內(nèi)部最高的 inline-box 相等,因而line-box 可以被撐高,從上往下排一個(gè)個(gè)排列下來,從而失去了居中的效果。換句話說,子元素溢出時(shí),父容器可以自適應(yīng)于內(nèi)部。
其他:line-height和font-size是一個(gè)可以繼承的屬性,在這種方案里面,必然會導(dǎo)致line-height、font-size被繼承,因此在需要排版子容器時(shí),需要復(fù)寫line-height、font-size。
2.2 子容器所在的line-box的line-height=父容器的height
對上面的方法不兼容IE6\7且不能做到父容器自適應(yīng)的方面,可以這樣改進(jìn):
- 把子容器當(dāng)成行內(nèi)元素呈遞
- 構(gòu)建一個(gè)行內(nèi)級別的鉤子元素,緊挨著子容器,以使用
:before/:after偽元素自制一個(gè)文本節(jié)點(diǎn)、或<span>或任意一個(gè)inline級別的標(biāo)簽、或者一個(gè)1*1的圖片。
- 讓鉤子元素?fù)螡M容器高度,這意味著它需要成為
inline-box元素,然后設(shè)置height:100%即可。
- 現(xiàn)在,子容器所在的 line-box 的 line-height = 父容器的 height。
- 最后,給子容器和鉤子元素設(shè)置
vertical-align:middle,讓它們的中心線對齊于于父容器的middle-line,就能做到垂直居中。
http:///humphry/86dsC/21/
現(xiàn)在這個(gè)布局可以自動生成,詳見@林小志的css小工具:圖片垂直居中span。
(如果需要水平居中,那么這個(gè)額外的節(jié)點(diǎn),需要移去自身所占的距離,一般使用margin-left:-1px或margin-right:-1px,取決于鉤子在子容器的哪一邊。)
說明:
兼容性:沒有涉及relative和absolute定位,布局時(shí)相對無痛。
兼容性:inline-box標(biāo)簽之間的任意數(shù)量的空白符:ASCII 空格 ( )、ASCII 制表符 (	)、ASCII 換頁符 ()、零寬度空格 (​) 會被瀏覽器解析成一個(gè)空格,造成間隙。需要通過改變HTML結(jié)構(gòu)/使用負(fù)margin等來去掉這個(gè)間隙。
兼容性:這個(gè)方案有一定hack量,去掉inline-box的空格間隙是一部分,display:inline-block的IE6\7 hack是另一部分。
自適應(yīng):父容器可以自適應(yīng)于外部了,因?yàn)檫@里不需要在任何地方知道父容器的高度。
溢出:子容器溢出時(shí)會變成頂對齊,原因同上。
3. 普通流,塊級table布局 (css2.1)
| 方案 |
兼容性 |
父容器 |
子容器 |
子容器溢出 |
其他 |
|
IE8 |
IE6/7 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
撐高 |
被截?cái)?/td>
| 不需手工計(jì)算 |
| 父容器display:table-cell,子容器vertical-align |
√ |
× |
√ |
× |
× |
√ |
√ |
√ |
√ |
× |
√ |
| 子容器被table、tr、td包裹 |
√ |
√ |
√ |
√* |
√* |
√ |
√ |
√ |
√ |
× |
√ |
3.1. 父容器display:table-cell,子容器vertical-align
這個(gè)方案依然是用到vertical-align:middle,只不過需要放到作為display:table-cell的元素之上。
http:///humphry/7AMF9/2/
這個(gè)布局也可以自動化生成:見@林小志的css小工具:圖片垂直居中 table cell
說明:
兼容性:IE6/7不兼容display:table-cell。
自適應(yīng):子容器無法自適應(yīng)于父容器,高度無法使用百分比單位,因?yàn)楦鶕?jù)渲染規(guī)則,display:table-cell的元素的包含塊是它父級的display:table的元素。
溢出:父元素就算給定高度,設(shè)置overflow,也不會導(dǎo)致溢出隱藏;在子元素溢出的時(shí)候,父容器不能保有自身設(shè)置的高度,直接會被撐高。
其他:display:tabel-cell本身讓很多屬性無效。
3.2. 子容器被table、tr、td包裹
為了可以讓子容器有百分比高度,我們可以直接構(gòu)建一個(gè)表結(jié)構(gòu)出來:
http:///humphry/Ns4RK/8/
說明:
兼容性:這是一個(gè)全兼容的方案。
自適應(yīng):現(xiàn)在“父級”的自適應(yīng)要求都可以得到滿足,只不過這里的“父級”指的是包在最外層的table。
溢出:父元素就算給定高度,設(shè)置overflow,也不會導(dǎo)致溢出隱藏。
其他:文檔結(jié)構(gòu)變復(fù)雜了,語義被拋棄了。
其他:display:tabel-cell本身讓很多屬性無效。
其他:可以把這個(gè)方案里的<table>換成display:table的其他元素,tr、td亦然: http:///humphry/KxKc8/
個(gè)人覺得……徒增煩惱爾。
4. 絕對定位布局 (css2.1)
| 方案 |
兼容性 |
父容器 |
子容器 |
子容器溢出 |
其他 |
|
IE8 |
IE6/7 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
撐高 |
被截?cái)?/td>
| 不需手工計(jì)算 |
| 子容器絕對定位,top:50%,負(fù)margin |
√ |
√ |
√ |
√* |
√ |
√ |
× |
× |
× |
√ |
× |
| 子容器絕對定位,top:0,bottom:0,margin:auto |
√ |
× |
√ |
√* |
√ |
√ |
× |
√ |
× |
√ |
√ |
4.1 子容器絕對定位,top:50%,負(fù)margin
.outer{ position: relative; }
.inner{ position: absolute; top: 50%; height: 20px; margin: -10px; }
這是互聯(lián)網(wǎng)上能找到的最多的關(guān)于垂直居中的方法。我們不相信瀏覽器,使用手算,將子元素挪去自身高度的50%。
說明:
兼容性:子元素和父元素都需要設(shè)置position,這就意味著IE6\7下面的數(shù)十個(gè)友情附贈的美好bug。
自適應(yīng):子元素必須定高,不定高算不出來負(fù)margin。不可以是百分比高度單位,因?yàn)?code>margin-top的百分比,是相對其包含塊的寬度而言的。
自適應(yīng):父容器可以自適應(yīng)于內(nèi)部,只不過不是這個(gè)子元素,而是父容器內(nèi)部其他的元素。子元素對外層高度、寬度塌陷,不能撐寬/撐高父容器了。
溢出:這個(gè)方案也可以支持子元素高度溢出的情形。
其他:需要手動計(jì)算子元素1/2的高度是多少。
4.2 子容器絕對定位,top:0,bottom:0,margin:auto
.outer{ position: relative; }
.inner{ position: absolute; margin-top: auto; margin-bottom : auto;
top: 0; bottom: 0; height: 20px; }
這個(gè)的原理寫在CSS2.1中:
‘top’ + ‘margin-top’ + ‘border-top-width’ + ‘padding-top’ + ‘height’ + ‘padding-bottom’ + ‘border-bottom-width’ + ‘margin-bottom’ + ‘bottom’ = 包含塊的高度
在其他值不是auto的時(shí)候,margin-top和margin-bottom是可以根據(jù)上式算出的,原理類似于水平居中。
說明:
兼容性:這個(gè)方案僅僅支持IE8+。IE6和IE7由于對同時(shí)定義top、bottom屬性的樣式解析與 css2.1 不一致,不支持這種定位方式。
自適應(yīng):父容器可以自適應(yīng)于內(nèi)部,只不過不是這個(gè)子元素,而是父容器內(nèi)部其他的元素。原因同上。
自適應(yīng):這個(gè)方案需要子容器有一個(gè)固定的高,或百分比自適應(yīng)于外部。它的高度不能是height:auto,因?yàn)檫@樣會使得上面的算式里auto出現(xiàn)在三個(gè)地方,瀏覽器無法計(jì)算出相應(yīng)margin值。
溢出:這個(gè)方案也可以支持子元素高度溢出的情形。
其他:完全不用算,耶!
5. css3一系列布局
| 方案 |
兼容性 |
父容器 |
子容器 |
子容器溢出 |
其他 |
|
IE8 |
IE6/7 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
定高 |
自適應(yīng)于內(nèi)部 |
自適應(yīng)于外部 |
撐高 |
被截?cái)?/td>
| 不需手工計(jì)算 |
| 子容器絕對定位,top:50%,translateY(-50%) |
× |
× |
√ |
√* |
√ |
√ |
√ |
√ |
× |
√ |
√ |
| backgournd代圖片backgournd-size |
× |
× |
√ |
√ |
√ |
√ |
√* |
√ |
× |
√ |
√ |
| flexbox |
× |
× |
5.1 子容器絕對定位,top:50%,translateY(-50%)
.outer{ position: relative; }
.inner{ position: absolute; top: 50%; transform: translateY(-50%);}
原理同上,僅僅是僅僅是用translate替換了負(fù)margin,因?yàn)?code>translate的百分比偏移量是容器本身的。
說明:
兼容性:ie9+(但,從好處來講,其實(shí)這個(gè)兼容性已經(jīng)完全不需要考慮IE6~8的相對/絕對布局bug了)
自適應(yīng):子元素可以不指定高度,也可以相對父級高寬做百分比設(shè)置,也包括定寬,非常靈活
自適應(yīng):父容器可以自適應(yīng)于內(nèi)部,只不過不是這個(gè)子元素,而是父容器內(nèi)部其他的元素。原因同上。
溢出:支持子元素溢出隱藏,或者溢出顯示。
其他:你會讓代碼陷入一個(gè)前綴的海洋……
.inner{
-webkit-transform:translate(-50%, -50%);
-moz-transform:translate(-50%, -50%);
-ms-transform:translate(-50%, -50%);
-o-tranform:translate(-50%, -50%);
transform:translate(-50%, -50%);
}
5.2 backgournd代圖片,backgournd-size
.inner{ background-image: url("...") ; background-size: cover ; height: 100% ; }
說明:
兼容性:只能是IE9+和現(xiàn)代瀏覽器
自適應(yīng):背景可以任意自適應(yīng)于外部容器,或者定高寬。也可以讓子容器自適應(yīng)于內(nèi)部內(nèi)容,背景自適應(yīng)于子容器。
溢出:支持背景溢出隱藏,但無法溢出顯示。好消息是,可以使用background-clip指定背景從哪里消失。
其他:你甚至可以把.inner的標(biāo)簽省掉,直接把背景放到.outer之上
5.3 flexbox
// TBD
// 很抱歉,由于對flexbox沒有深入的了解,我還沒有試驗(yàn)出flexbox在子元素溢出時(shí)也能保持居中的解決方案……最好的結(jié)果是子元素被拉伸(= =)。有人有過實(shí)例嗎?
說明:
兼容性:只能是IE9+和現(xiàn)代瀏覽器
其他:兼容性是我目前已知的東西……
|