1. 水平垂直居中問題
這可以說是最經(jīng)典的問題了姆涩,水平垂直居中账忘,這個(gè)問題從入門前端一直到面試菠齿,甚至到工作之后都會時(shí)不時(shí)遇到佑吝,最近的面試也被問過這之類的問題,這里還是好好總結(jié)一番绳匀,以作備忘芋忿。HTML 部分:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS居中</title>
</head>
<body>
<!-- <div class="grandfather"> -->
<div id="parent">
<div id="child"></div>
</div>
<!-- </div> -->
</body>
</html>
方法一:
設(shè)置子元素 position 為 absolute 炸客,然后讓 top 和 left 都為 50%,再使 margin 為子元素的長和寬的負(fù) 1/2 即可:
說明: 這種方法局限性很大戈钢,不做推薦痹仙。
注意: 此處父元素得添加 postion:relative/absolute ,因?yàn)?absolute 定位是基于最近的設(shè)有 position 的父元素進(jìn)行定位殉了,如果父元素沒有設(shè)置 position 开仰,則其會基于 body 定位。但是如果 body 也沒有的設(shè)置 position 的話則基于視口高度計(jì)算位置:document.documentElement.clientHeight 薪铜≈诠可以試試去掉 parent 的 position 會發(fā)生很奇妙的事呢!
附加: 關(guān)于子元素 position 設(shè)置為 relative 而引發(fā)的問題隔箍。如果此時(shí)父元素的 position 為 absolute谓娃,那么不會有什么奇怪的現(xiàn)象發(fā)生;但是如果此時(shí)父元素的 position 為 relative 蜒滩,那么你會發(fā)現(xiàn)父元素會向上方移動一點(diǎn)距離,這又是為什么呢滨达??俯艰?這涉及到外邊距塌陷(margin-collapse)問題了捡遍,這點(diǎn)在后面的一些2. 外邊距問題中去解釋了(提前透露:父級向上移動了-50px)。
推薦指數(shù): ★
#parent {
background-color: black;
position: relative; /*或者absolute*/
height: 300px;
width: 300px;
}
#child {
background-color: #ccc;
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -50px;
height: 100px;
width: 100px;
}
方法二:
CSS里面還有一種 display: table-cell 的屬性蟆炊,我們可以將父元素的 display 設(shè)置為 table-cell 屬性稽莉,然后將子元素的 display 設(shè)置為 inline 或者 inline-block ,此時(shí)便可以用我們熟悉的 text-align: center 和 vertical-align: middle 來使得子元素水平垂直居中了涩搓。
說明: 這種方法最適合子元素都為行內(nèi)元素(或者帶有行內(nèi)元素性質(zhì)的塊級元素)的布局污秆,故而可以根據(jù)情況選用。
推薦指數(shù): ★★★☆
#parent {
background-color: black;
display: table-cell;
text-align: center;
vertical-align: middle;
height: 300px;
width: 300px;
}
#child {
background-color: #ccc;
display: inline-block;
height: 100px; /*可以改為33.3%*/
width: 100px; /*可以改為33.3%*/
}
方法三:
方法一中只能解決子元素定大小問題昧甘,有時(shí)候子元素大小變化可以用以下方法解決:
說明: 這種方法非常棒良拼,兼容性也很不錯(cuò),強(qiáng)推充边!
附加: 關(guān)于子元素的 position 設(shè)為 relative 而使得垂直居中無效問題庸推。關(guān)于這個(gè)問題其實(shí)很好理解,因?yàn)榻奖琾ostion: relative 的移動是基于自身原本的位置嘛贬媒,top 、 bottom 肘习、 left 际乘、 right 都為 0 ,換句話說就是位置不動漂佩,然而脖含,你有見過塊級元素能夠 margin: auto 上下自動補(bǔ)齊的嗎罪塔!沒有吧~ 故而起作用的只有左右自動計(jì)算補(bǔ)齊而已,也就只有水平居中對齊了养葵!
推薦指數(shù): ★★★★★
#parent {
background-color: black;
position: relative;
height: 300px;
width: 300px;
}
#child {
background-color: #ccc;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 33.3%;
width: 33.3%;
}
方法四:
由于CSS3的來臨征堪,我們也應(yīng)該跟上時(shí)代的潮流,故而對上述方法有有所改進(jìn)关拒,在此我們可以嘗試一下 transform 元素的 translate 2D 平移佃蚜,讓其想著 X 、Y 軸負(fù)方向移動自身長度的一半距離即可達(dá)到效果夏醉。
說明: 畢竟技術(shù)向新的方向發(fā)展爽锥,可以多嘗試一下新技術(shù),推薦畔柔。
注意: 由于瀏覽器的支持性問題氯夷,使用的時(shí)候可以檢測 CSS 支持性,也可以通過 Autoprefixer CSS online 來寫兼容性代碼靶擦。
附加: 關(guān)于 CSS3 開啟 GPU 硬件加速提升網(wǎng)站動畫渲染性能問題腮考。這一點(diǎn)不知道對此位置平移轉(zhuǎn)換有沒幫助,不過當(dāng)做是拓展來介紹了玄捕,在此我就不詳細(xì)說了踩蔚,推薦看 CSS3 頁面渲染加速。
推薦指數(shù): ★★★★
#parent {
background-color: black;
position: relative;
height: 300px;
width: 300px;
}
#child {
background-color: #ccc;
position: absolute; /*或者relative*/
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 33.3%;
width: 33.3%;
}
方法五:
這個(gè)是另外一種 CSS3 的解決方法(我比較喜歡)枚粘,即 flex 布局馅闽。通過設(shè)置父元素的 display 屬性為 flex,然后設(shè)置其 align-content 和 justify-content 來使得子元素能夠水平垂直居中馍迄。
說明: 這個(gè)方法在布局上非常靈活福也,在不考慮兼容的情況下,強(qiáng)烈推薦此方法進(jìn)行布局攀圈。這真的是個(gè)非常棒的布局方法暴凑!
注意: 由于瀏覽器的支持性問題,使用的時(shí)候可以檢測 CSS 支持性赘来,也可以通過 Autoprefixer CSS online 來寫兼容性代碼现喳。
附加: 關(guān)于flex的詳細(xì)介紹可以參考阮老師的博客: Flex 布局語法篇和 Flex 布局實(shí)例篇。
推薦指數(shù): ★★★★☆
#parent {
background-color: black;
display: flex;
display: -webkit-flex; /*Safari*/
align-content: center;
-webkit-align-items: center;
justify-content: center;
-webkit-justify-content: center;
height: 300px;
width: 300px;
}
#child {
background-color: #ccc;
height: 33.3%;
width: 33.3%;
}
2. 外邊距問題
現(xiàn)象描述
第一種情形是犬辰,當(dāng)你在一個(gè) div 元素內(nèi)插入一個(gè)塊級子元素嗦篱,然后設(shè)置其 margin-top 值,這是你會發(fā)現(xiàn)并不是子元素在父元素內(nèi)撐開了一段距離幌缝,而是父元素向上撐開了一段距離默色。
原以為: 實(shí)際上:
body body
-------------------- --------------------
parent *
----------- | 50px
* *
| 50px parent
* -----------
child child
----------- -----------
parent parent
-------------------- --------------------
body body
另一種情況是,當(dāng)你在 div 元素內(nèi)插入多個(gè)塊級元素,你給其中相鄰的兩個(gè)設(shè)置 margin-top 和 margin-bottom 腿宰,你會發(fā)現(xiàn),他們之間的距離為相對應(yīng)的 margin-top 和 margin-bottom 中的最大值缘厢,當(dāng)設(shè)置第一個(gè)塊級元素的 margin-top 屬性則會像第一種情形那樣吃度,父級框移動了。
原以為:
parent
------------------------
| 10px
childA
| 10px ==> | 30px
| 20px ==> |
childB
| 20px
------------------------
parent
實(shí)際上:
body
--------------------------
| 10px
parent
----------------
childA
| max(10px, 20px) ==> 20px
childB
| 20px
----------------
parent
--------------------------
body
問題分析
詳細(xì)解答在官方文檔的 8.3.1 合并 margin 這一節(jié)贴硫,太長了椿每,我就不復(fù)制了,但是簡單來說可以用W3C上的話來總結(jié):
外邊距合并指的是英遭,當(dāng)兩個(gè)垂直外邊距相遇時(shí)间护,它們將形成一個(gè)外邊距。
合并后的外邊距的高度等于兩個(gè)發(fā)生合并的外邊距的高度中的較大者挖诸。
這樣就清楚了汁尺!下面說說我的理解(很實(shí)用):
- Case 1:
父親與兒子并排站(沒邊界嘛,父親位置不定)多律,兒子說:我站在相對前方目標(biāo) 1m 的地方痴突,問:父親站在離前方目標(biāo)多遠(yuǎn)處?
顯然是 1m 的地方嘛狼荞!因?yàn)槟繕?biāo)不明確(父親也是相對嘛)辽装,故而父親同樣也是站在相對前方 1m 處。 - Case 2:
ChildA: 我站在距離 ChildB 1m 的位置相味。
ChildB: 我站在距離 ChildA 1m 的位置拾积。
問: ChildA、ChildB距離多遠(yuǎn)丰涉?
根據(jù)相對性拓巧,這不就是 1m 嘛!
依次類比昔搂,很相似吧(沒邊界擋著 == 站在同一起跑線)玲销! (σ???)σ..:*☆ 哎喲不錯(cuò)哦!U贤斜!
問題解決
其實(shí)分析時(shí)已經(jīng)表明了,因?yàn)槎颊驹谕黄鹋芫€逛裤,故而大家都不分先后瘩绒,要讓它們分開,設(shè)置點(diǎn)障礙就行了带族,如:
情況 1:
Method 1: 給父元素添加 border: 1px solid #xxxxxx(劃分界限锁荔,父子沒并排站,兒子前方有了目標(biāo))蝙砌。
Method 2: 給父元素添加 overflow: hidden 屬性(相當(dāng)于父級給自己定了一個(gè)隱藏邊界)阳堕。
Method 3:讓父元素為絕對定位(因?yàn)橄喈?dāng)于讓父元素站在一個(gè)定點(diǎn)跋理,子元素的前方目標(biāo)明確了,是父親相對自己距離為 0恬总,距離也就拉開了)前普。
Method 4:為父元素聲明浮動(浮動會脫離文檔流,此時(shí)瀏覽器會給頂元素位置壹堰,即所能達(dá)到的最左上方)拭卿。
個(gè)人推薦: Method 2。
這個(gè)情況可以說是前一個(gè)情況的完整版贱纠,其實(shí)整體上就是BFC問題(前者也是)峻厚,BFC的詳細(xì)內(nèi)容我就不細(xì)說了,推薦 BFC 神奇背后的原理谆焊。
情況 2:
首先得讓父級元素按照情況 1中方式處理惠桃,其次是處理子元素。
Method 1:自己計(jì)算好相鄰元素的距離懊渡,然后直接設(shè)置刽射。
Method 2:給每個(gè)子元素添加一個(gè) wrapper ,使得每個(gè)子元素都是 BFC 區(qū)域剃执。
個(gè)人推薦: Method 2誓禁。
3. 奇怪的布局問題
現(xiàn)象描述
當(dāng)你在 div 元素內(nèi)插入多個(gè)行內(nèi)塊級元素,你給其中任意一個(gè)或者多個(gè)設(shè)置 margin-top 想要使得它/它們表現(xiàn)得與眾不同肾档,可是摹恰,到頭來所有元素都會移動,而且唯一準(zhǔn)確的只有所設(shè)值最大的那個(gè)子元素怒见,其他子元素則混淆俗慈。
(注:所有子元素高度為 height: 50px)
原以為:
parent
----------------------------------------------------
| 50px childB | 25px
childA childC
----------------------------------------------------
parent
實(shí)際上:
parent
----------------------------------------------------
| 50px | 50px + 25px | 50px - 25px
childA childB childC
----------------------------------------------------
parent
問題分析
其實(shí)這個(gè)問題和IFC問題很相似,也就是行內(nèi)元素基線的選擇問題遣耍。 IFC 的介紹中有這么一段話:
IFC ( Inline Formatting Contexts )直譯為"內(nèi)聯(lián)格式化上下文"闺阱,IFC 的 line box(線框)高度由其包含行內(nèi)元素中最高的實(shí)際高度計(jì)算而來(不受到豎直方向的 padding/margin 影響)
IFC 中的 line box 一般左右都貼緊整個(gè) IFC ,但是會因?yàn)?float 元素而擾亂舵变。
這就說到很明白了酣溃。由于這里是 display: inline-block 故而,其線框高度包含了 margin 纪隙、 border 赊豌、 padding 、 content 绵咱,這樣就知道為什么其他元素的 margin 后的結(jié)果與預(yù)期不一致的問題了碘饼。(此處把 childC 的高度變?yōu)?300xp 則是以 childC 的基線為基準(zhǔn)定位其他兄弟元素,因?yàn)榇藭r(shí) childC “最高”)。
問題解決
要解決這個(gè)問題,想讓不同子元素呈現(xiàn)不同效果蒂萎,我們可以給每個(gè)子元素添加一個(gè) wrapper 秆吵,并讓 wrapper 以 BFC 的形式包裹子元素,然后你就可以肆無忌憚的改變每個(gè)子元素位置了N宕取(參考1. 水平垂直居中問題)
/*wrapper style*/
.wrapper {
display: inline-block;
height: 100%; /*讓 wrapper 基線對齊*/
overflow: hidden; /* BFC */
}
其他問題: 為什么其他子元素的 margin: mpx 不是在基線上位置向下移動 mpx 而是向上移動呢?(m > 0)
這個(gè)問題其實(shí)也很好說明主穗,由于此處默認(rèn)對齊方式是都是基于 bottom 的泻拦,故而都是在 bottom 這條起始線開始變化(可以打開瀏覽器看盒子模型), childB 由于沒有 margin 故而其底邊線為此時(shí)公用的底邊界忽媒,其他盒子在此基礎(chǔ)上有 margin-bottom 的開始向上移動(“最高”的盒子會撐高整個(gè)高度争拐,其它盒子只會上升 margin-bottom 值),因此會出現(xiàn)此現(xiàn)象晦雨。
此題代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS外邊距塌陷</title>
</head>
<style type="text/css">
#parent {
background-color: black;
height: 500px;
overflow: hidden;
width: 500px;
}
#parent div {
display: inline-block; /*包含了 wrapper */
}
#childA {
background-color: greenyellow;
height: 100px;
margin: 25px 0;
width: 100px;
}
#childB {
background-color: aliceblue;
height: 100px;
width: 100px;
}
#childC {
background-color: orangered;
height: 100px;
margin: 50px 0;
width: 100px;
}
.wrapper {
height: 100%;
overflow: hidden;
}
</style>
<body>
<div id="parent">
<div class="wrapper">
<div id="childA"></div>
</div>
<div class="wrapper">
<div id="childB"></div>
</div>
<div class="wrapper">
<div id="childC"></div>
</div>
</div>
</body>
</html>