建議在PC端閱讀
本文面向?qū)ο螅簩?duì)標(biāo)題中的概念基本不了解或僅僅聽(tīng)說(shuō)過(guò)名字的人。如果已有一定了解請(qǐng)直接拉到最下看推薦閱讀
另外其實(shí)這是個(gè)很大的話題共螺,這里僅當(dāng)拋磚引玉,并提供進(jìn)一步閱讀的文章
一些demo
我們先從一些現(xiàn)象開(kāi)始入手吧↓(建議在另一窗口打開(kāi)示例情竹,一邊修改示例一邊閱讀效果更佳)
本文示例
例一
第一個(gè)例子是最常見(jiàn)的一種邊界折疊(Margin Collapse)。一般我們很容易忽略掉這個(gè)現(xiàn)象匀哄,因?yàn)樗臀覀兇蠖鄶?shù)情況下的直覺(jué)比較符合秦效。
例如第一個(gè)例子里的兩個(gè)p標(biāo)簽,它們的關(guān)系是并列的涎嚼,所以我們?cè)O(shè)置margin:20px 0;
時(shí)心里其實(shí)是想讓兩個(gè)p標(biāo)簽之間相隔20px(事實(shí)上現(xiàn)在也是這樣表現(xiàn)的)阱州。
但有時(shí)候我們并不想讓它們折疊(我們希望“修正”的同時(shí),保持使用margin并盡量少添加難以理解的CSS屬性)法梯,那要怎么做呢苔货?(即需要清楚產(chǎn)生邊界折疊的條件)
例子中提示的做法是用一個(gè)設(shè)置了
overflow:hidden;
的div將其包裹犀概,但是overflow:hidden;
作用明明是剪裁溢出的內(nèi)容,為何能產(chǎn)生取消邊界折疊的效果呢夜惭?直接給出這樣的解決方案讓人難以理解而且容易忘記姻灶。
(似乎只能通過(guò)外加BFC創(chuàng)造不同的BFC來(lái)避免...)
例二
如果說(shuō)第一個(gè)例子一定程度上還算符合直覺(jué),那第二個(gè)例子的情況老實(shí)說(shuō)我是不能理解的...一度讓我以為是出了bug...但了解了Margin Collapse的產(chǎn)生條件之后,相信你能明白為何會(huì)出現(xiàn)這種現(xiàn)象以及如果消除它诈茧。
例三
第三個(gè)例子主要和BFC有關(guān)产喉。一般說(shuō)到"清除浮動(dòng)"就會(huì)想到clear:both;
overflow:hidden;
之類(lèi).但是給父元素設(shè)置浮動(dòng)居然也能"清除浮動(dòng)"???估計(jì)深受浮動(dòng)折磨的人看到這現(xiàn)象會(huì)進(jìn)一步崩潰:為什么這樣也能清除浮動(dòng)?到底有多少種清除浮動(dòng)的方法敢会?(當(dāng)然“清除浮動(dòng)”不是本文重點(diǎn)曾沈,若想進(jìn)一步了解請(qǐng)看推薦閱讀)
所以這些現(xiàn)象到底跟標(biāo)題中的Margin Collapse、BFC有什么關(guān)系呢鸥昏?
上面例子的現(xiàn)象你都能給出解釋和解決方案嗎塞俱?如果都沒(méi)問(wèn)題的話恭喜你,可以直接下拉到最后看推薦閱讀進(jìn)行進(jìn)一步閱讀了吏垮。如果一頭霧水的話也沒(méi)關(guān)系障涯,我們一步一步來(lái)看是怎么回事。
想一下惫皱,有沒(méi)有覺(jué)得這有點(diǎn)像讀英語(yǔ)時(shí)每個(gè)單詞都認(rèn)識(shí)像樊,連在一起就看不懂的情況?這是因?yàn)槿狈ο嚓P(guān)的語(yǔ)法知識(shí)(當(dāng)然也有可能是缺少上下文)
每一條CSS Rule就像一個(gè)個(gè)單詞旅敷,而相應(yīng)的語(yǔ)法就是w3c制定的布局方案了(當(dāng)然這里的類(lèi)比不完全準(zhǔn)確生棍,自然語(yǔ)言是與上下文有關(guān)的語(yǔ)法,而CSS是與上下文無(wú)關(guān)的語(yǔ)法)
那w3c是怎么規(guī)定Margin Collapse的呢媳谁?
產(chǎn)生折疊的必備條件:margin必須是鄰接的!
而根據(jù)w3c規(guī)范涂滴,兩個(gè)margin是鄰接的必須滿足以下條件:
1.必須是處于常規(guī)文檔流(非float和絕對(duì)定位)的塊級(jí)盒子,并且處于同一個(gè)BFC(BFC產(chǎn)生條件見(jiàn)下文)當(dāng)中。
2.沒(méi)有線盒晴音,沒(méi)有空隙(clearance柔纵,本文不涉及),沒(méi)有padding和border將他們分隔開(kāi)
3.都屬于垂直方向上相鄰的外邊距锤躁,可以是下面任意一種情況
- 元素的margin-top與其第一個(gè)常規(guī)文檔流的子元素的margin-top(即例二)
- 元素的margin-bottom與其下一個(gè)常規(guī)文檔流的兄弟元素的margin-top(即例一)
- height為auto的元素的margin-bottom與其最后一個(gè)常規(guī)文檔流的子元素的margin-bottom
- 高度為0并且最小高度也為0搁料,不包含常規(guī)文檔流的子元素,并且自身沒(méi)有建立新的BFC的元素的margin-top和margin-bottom
以上的條件意味著下列的規(guī)則:
- 創(chuàng)建了新的BFC的元素(BFC元素的產(chǎn)生條件見(jiàn)下文)與它的子元素的外邊距不會(huì)折疊
- 浮動(dòng)元素不與任何元素的外邊距產(chǎn)生折疊(包括其父元素和子元素)
- 絕對(duì)定位元素不與任何元素的外邊距產(chǎn)生折疊
- inline-block元素不與任何元素的外邊距產(chǎn)生折疊
- 一個(gè)常規(guī)文檔流元素的margin-bottom與它下一個(gè)常規(guī)文檔流的兄弟元素的margin-top會(huì)產(chǎn)生折疊系羞,除非它們之間存在間隙(clearance)郭计。
- 一個(gè)常規(guī)文檔流元素的margin-top 與其第一個(gè)常規(guī)文檔流的子元素的margin-top產(chǎn)生折疊,條件為父元素不包含 padding 和 border 椒振,子元素不包含 clearance昭伸。
- 一個(gè) 'height' 為 'auto' 并且 'min-height' 為 '0'的常規(guī)文檔流元素的 margin-bottom 會(huì)與其最后一個(gè)常規(guī)文檔流子元素的 margin-bottom 折疊,條件為父元素不包含 padding 和 border 澎迎,子元素的 margin-bottom 不與包含 clearance 的 margin-top 折疊庐杨。
一個(gè)不包含border-top选调、border-bottom、padding-top灵份、padding-bottom的常規(guī)文檔流元素仁堪,并且其 'height' 為 0 或 'auto', 'min-height' 為 '0'各吨,其里面也不包含行盒(line box)枝笨,其自身的 margin-top 和 margin-bottom 會(huì)折疊。
折疊的結(jié)果:
兩個(gè)相鄰的外邊距都是正數(shù)時(shí)揭蜒,折疊結(jié)果是它們兩者之間較大的值横浑。
兩個(gè)相鄰的外邊距都是負(fù)數(shù)時(shí),折疊結(jié)果是兩者絕對(duì)值的較大值屉更。
兩個(gè)外邊距一正一負(fù)時(shí)徙融,折疊結(jié)果是兩者的相加的和。
摘自w3plus: http://www.w3cplus.com/css/understanding-bfc-and-margin-collapse.html ? w3cplus.com
BFC(Block Formatting Contexts瑰谜,塊級(jí)上下文)的產(chǎn)生條件
BFC在CSS3中改為Flow Root欺冀,觸發(fā)條件多了下面加粗的部分
- 浮動(dòng)元素,float 除 none 以外的值
- 絕對(duì)定位元素萨脑,position(absolute隐轩,fixed)
- display 為以下其中之一的值 inline-blocks,table-cells渤早,table-captions
- overflow 除了 visible 以外的值(hidden职车,auto,scroll)
注:"display:table" 本身并不產(chǎn)生 BFC鹊杖,但它會(huì)產(chǎn)生匿名框悴灵,匿名框中包含 "display:table-cell" 的框會(huì)產(chǎn)成 BFC。
好的骂蓖,上面一口氣丟了一大堆定義和條件出來(lái)积瞒,估計(jì)直接強(qiáng)行讀下來(lái)已經(jīng)有點(diǎn)暈了。沒(méi)關(guān)系我們結(jié)合文章開(kāi)頭的例子看一下吧登下。
例子解析
例一
顯然例一符合折疊的第一個(gè)條件:
1.必須是處于常規(guī)文檔流(非float和絕對(duì)定位)的塊級(jí)盒子,并且處于同一個(gè)BFC(BFC產(chǎn)生條件見(jiàn)下文)當(dāng)中茫孔。
那要取消折疊自然就是破壞第一個(gè)條件啦,于是第一反應(yīng)就是用overflow:hidden;
來(lái)把p標(biāo)簽變成BFC被芳。然后你就會(huì)開(kāi)心的發(fā)現(xiàn)......并沒(méi)有什么變化银酬。
為什么呢?我們?cè)賮?lái)讀一下第一個(gè)條件
"....處于同一個(gè)BFC當(dāng)中筐钟。"
嗯,這里要搞清楚一個(gè)概念赋朦,BFC是一個(gè)區(qū)域篓冲,不是一個(gè)元素李破。
也就是說(shuō),你給p標(biāo)簽設(shè)的overflow:hidden;
只是把p標(biāo)簽里的內(nèi)容設(shè)置成一個(gè)新的BFC壹将,但是這個(gè)p標(biāo)簽仍處于它之前所在BFC中這個(gè)事實(shí)沒(méi)有改變嗤攻。
所以給出的解決方案是外面多包裹一個(gè)div再把這個(gè)div變成BFC(如overflow:hidden;
),這樣被包裹的p標(biāo)簽處于它的父元素創(chuàng)建的BFC,而它的父元素此時(shí)和另一個(gè)p標(biāo)簽處于同一個(gè)BFC诽俯,所以被包裹的p標(biāo)簽與另一個(gè)p標(biāo)簽自然不處于同一個(gè)BFC了(表達(dá)能力有限妇菱,這段寫(xiě)的有點(diǎn)繞)
雖然要多加一個(gè)標(biāo)簽,但其實(shí)仔細(xì)想想也沒(méi)有破壞語(yǔ)義性暴区。因?yàn)檎嵌嗉恿艘粋€(gè)標(biāo)簽才表示了被包裹的內(nèi)容與外面的內(nèi)容不處于同一個(gè)上下文闯团。
例二
例二其實(shí)也是因?yàn)樘幱谕粋€(gè)BFC中而導(dǎo)致的,你可以試著給.parent加上overflow:hidden;
看看效果仙粱。demo中讓你嘗試加border或padding主要是因?yàn)檫@個(gè)折疊條件比較容易忽略房交。
不過(guò)產(chǎn)生BFC的條件多多少少都會(huì)有副作用,而這里其實(shí)只要讓子元素margin與父元素marigin不相鄰(折疊的最基本要求)就可以了
這里推薦的解決方案是使用偽元素:
.fathet:before{
content:"";
display: table;
}
首先content是偽元素生成的必要條件
這里的原理是:display:table;
會(huì)生成包含display:table-cell;
的匿名框(見(jiàn)上文BFC產(chǎn)生條件)伐割,從而產(chǎn)生了一個(gè)“沒(méi)有高度”的BFC區(qū)域(個(gè)人理解候味,如有錯(cuò)誤歡迎指出)將子元素和父元素的margin分割開(kāi)了。
例三
這里主要是因?yàn)锽FC的特性所致
BFC有如下特性:
- BFC可以包含浮動(dòng)元素
w3c原文:“'Auto' heights for block formatting context roots”隔心,也就是 BFC 會(huì)根據(jù)子元素的情況自動(dòng)適應(yīng)高度白群,即使其子元素中包括浮動(dòng)元素。
- BFC可以阻止元素被浮動(dòng)元素覆蓋(即取消文字環(huán)繞的效果)
所以例三給父元素設(shè)置浮動(dòng)后高度不再坍塌只是因?yàn)楦冈氐漠a(chǎn)生了BFC硬霍,而用其他產(chǎn)生BFC的方法同樣能包含浮動(dòng)元素帜慢。
值得注意的是,雖然BFC有閉合浮動(dòng)的效果须尚,但并不推薦利用BFC閉合浮動(dòng)崖堤。因?yàn)槊恳粋€(gè)用于產(chǎn)生BFC的條件都有一定程度的副作用手趣,盡管當(dāng)時(shí)可能副作用不明顯乌昔,但也是個(gè)很大的隱患。
推薦的“清除浮動(dòng)”的方法是Nicolas Gallagher大神提出的micro clearfix hack:
.clearfix:after{
content:"";
display:table;
clear:both;
}
總結(jié)
- Margin Collapse不是bug臼予,而是在CSS布局規(guī)則里面有規(guī)定的撩轰,具體出現(xiàn)情況見(jiàn)上文胯甩。
- BFC是一個(gè)區(qū)域,或者說(shuō)是元素具有的一個(gè)特性堪嫂,而不是元素本身偎箫。
- BFC能閉合浮動(dòng),但不應(yīng)該用BFC來(lái)閉合浮動(dòng)皆串。
思考
先給body加上border淹办,方便觀察。
試著給body標(biāo)簽加上margin恶复,再給body的第一個(gè)子元素(需要是塊級(jí)的)設(shè)置margin怜森,看是否會(huì)發(fā)生折疊速挑?
如果會(huì)發(fā)生折疊,也就是符合條件1
1.必須是處于常規(guī)文檔流(非float和絕對(duì)定位)的塊級(jí)盒子,并且處于同一個(gè)BFC當(dāng)中副硅。
也就是處于同一個(gè)BFC中姥宝。這時(shí)如果給第一個(gè)子元素設(shè)置浮動(dòng)又會(huì)發(fā)生什么呢?
實(shí)驗(yàn)的結(jié)果是body無(wú)法包含浮動(dòng)元素恐疲,這與BFC的特性矛盾腊满。但同時(shí)卻能發(fā)生Margin Collapse,也就是處于同一個(gè)BFC中培己。這似乎有點(diǎn)矛盾碳蛋。
當(dāng)然我也沒(méi)想通這是為什么,如果有明白的人歡迎指教漱凝。我能想到的就只有因?yàn)閎ody標(biāo)簽太特殊疮蹦,這是瀏覽器對(duì)它作出的特殊處理......
推薦閱讀
四篇文章搞定BFC(建議依次閱讀)
- 詳說(shuō) Block Formatting Contexts (塊級(jí)格式化上下文)
- W3C的官方定義
- Understanding Block Formatting Contexts in CSS
- Formatting contexts