Margin Collapse、BFC外加一點(diǎn)點(diǎn)“清除浮動(dòng)”

建議在PC端閱讀
本文面向?qū)ο螅簩?duì)標(biāo)題中的概念基本不了解或僅僅聽說過名字的人。如果已有一定了解請(qǐng)直接拉到最下看推薦閱讀
另外其實(shí)這是個(gè)很大的話題,這里僅當(dāng)拋磚引玉,并提供進(jìn)一步閱讀的文章

一些demo

我們先從一些現(xiàn)象開始入手吧↓(建議在另一窗口打開示例,一邊修改示例一邊閱讀效果更佳)
本文示例

例一

第一個(gè)例子是最常見的一種邊界折疊(Margin Collapse)。一般我們很容易忽略掉這個(gè)現(xiàn)象,因?yàn)樗臀覀兇蠖鄶?shù)情況下的直覺比較符合。
例如第一個(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)生取消邊界折疊的效果呢?直接給出這樣的解決方案讓人難以理解而且容易忘記。

(似乎只能通過外加BFC創(chuàng)造不同的BFC來避免...)

例二

如果說第一個(gè)例子一定程度上還算符合直覺,那第二個(gè)例子的情況老實(shí)說我是不能理解的...一度讓我以為是出了bug...但了解了Margin Collapse的產(chǎn)生條件之后,相信你能明白為何會(huì)出現(xiàn)這種現(xiàn)象以及如果消除它。

例三

第三個(gè)例子主要和BFC有關(guān)。一般說到"清除浮動(dòng)"就會(huì)想到clear:both; overflow:hidden;之類.但是給父元素設(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)象你都能給出解釋和解決方案嗎?如果都沒問題的話恭喜你,可以直接下拉到最后看推薦閱讀進(jìn)行進(jìn)一步閱讀了。如果一頭霧水的話也沒關(guān)系,我們一步一步來看是怎么回事。

想一下,有沒有覺得這有點(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)然這里的類比不完全準(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)生條件見下文)當(dāng)中。
2.沒有線盒,沒有空隙(clearance,本文不涉及),沒有padding和border將他們分隔開
3.都屬于垂直方向上相鄰的外邊距,可以是下面任意一種情況

  • 元素的margin-top與其第一個(gè)常規(guī)文檔流的子元素的margin-top(即例二)
  • 元素的margin-bottom與其下一個(gè)常規(guī)文檔流的兄弟元素的margin-top(即例一)
  • height為auto的元素的margin-bottom與其最后一個(gè)常規(guī)文檔流的子元素的margin-bottom
  • 高度為0并且最小高度也為0,不包含常規(guī)文檔流的子元素,并且自身沒有建立新的BFC的元素的margin-top和margin-bottom

以上的條件意味著下列的規(guī)則:

  • 創(chuàng)建了新的BFC的元素(BFC元素的產(chǎ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。


好的,上面一口氣丟了一大堆定義和條件出來,估計(jì)直接強(qiáng)行讀下來已經(jīng)有點(diǎn)暈了。沒關(guān)系我們結(jié)合文章開頭的例子看一下吧。

例子解析

例一

顯然例一符合折疊的第一個(gè)條件:

1.必須是處于常規(guī)文檔流(非float和絕對(duì)定位)的塊級(jí)盒子,并且處于同一個(gè)BFC(BFC產(chǎn)生條件見下文)當(dāng)中。

那要取消折疊自然就是破壞第一個(gè)條件啦,于是第一反應(yīng)就是用overflow:hidden;來把p標(biāo)簽變成BFC。然后你就會(huì)開心的發(fā)現(xiàn)......并沒有什么變化。
為什么呢?我們?cè)賮碜x一下第一個(gè)條件

"....處于同一個(gè)BFC當(dāng)中。"

嗯,這里要搞清楚一個(gè)概念,BFC是一個(gè)區(qū)域,不是一個(gè)元素。
也就是說,你給p標(biāo)簽設(shè)的overflow:hidden;只是把p標(biāo)簽里的內(nèi)容設(shè)置成一個(gè)新的BFC,但是這個(gè)p標(biāo)簽仍處于它之前所在BFC中這個(gè)事實(shí)沒有改變。
所以給出的解決方案是外面多包裹一個(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á)能力有限,這段寫的有點(diǎn)繞)

雖然要多加一個(gè)標(biāo)簽,但其實(shí)仔細(xì)想想也沒有破壞語(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è)折疊條件比較容易忽略。
不過產(chǎn)生BFC的條件多多少少都會(huì)有副作用,而這里其實(shí)只要讓子元素margin與父元素marigin不相鄰(折疊的最基本要求)就可以了
這里推薦的解決方案是使用偽元素:

.fathet:before{
     content:"";
     display: table;
}

首先content是偽元素生成的必要條件
這里的原理是:display:table;會(huì)生成包含display:table-cell;的匿名框(見上文BFC產(chǎn)生條件),從而產(chǎn)生了一個(gè)“沒有高度”的BFC區(qū)域(個(gè)人理解,如有錯(cuò)誤歡迎指出)將子元素和父元素的margin分割開了。

例三

這里主要是因?yàn)锽FC的特性所致
BFC有如下特性:

  1. BFC可以包含浮動(dòng)元素

w3c原文:“'Auto' heights for block formatting context roots”,也就是 BFC 會(huì)根據(jù)子元素的情況自動(dòng)適應(yīng)高度,即使其子元素中包括浮動(dòng)元素。

  1. 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é)

  1. Margin Collapse不是bug,而是在CSS布局規(guī)則里面有規(guī)定的,具體出現(xiàn)情況見上文。
  2. BFC是一個(gè)區(qū)域,或者說是元素具有的一個(gè)特性,而不是元素本身。
  3. BFC能閉合浮動(dòng),但不應(yīng)該用BFC來閉合浮動(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)然我也沒想通這是為什么,如果有明白的人歡迎指教。我能想到的就只有因?yàn)閎ody標(biāo)簽太特殊,這是瀏覽器對(duì)它作出的特殊處理......


推薦閱讀

四篇文章搞定BFC(建議依次閱讀)

  1. 詳說 Block Formatting Contexts (塊級(jí)格式化上下文)
  2. W3C的官方定義
  3. Understanding Block Formatting Contexts in CSS
  4. Formatting contexts

BFC與Margin Collapse

深入理解BFC和Margin Collapse

搞定清除浮動(dòng)

那些年我們一起清除過的浮動(dòng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,774評(píng)論 1 92
  • 1.浮動(dòng)元素有什么特征?對(duì)父容器、其他浮動(dòng)元素、普通元素、文字分別有什么影響? 何謂浮動(dòng)元素?有什么特征?所謂浮動(dòng)...
    草鞋弟閱讀 823評(píng)論 0 1
  • 浮動(dòng)元素的特征,對(duì)父容器、對(duì)其他浮動(dòng)元素、普通元素、文字的影響。 浮動(dòng)會(huì)使應(yīng)用的元素脫離文檔流。按指定的位置來移動(dòng)...
    邢烽朔閱讀 666評(píng)論 2 7
  • 1.在什么場(chǎng)景下會(huì)出現(xiàn)外邊距合并?如何合并?如何不讓相鄰元素外邊距合并?給個(gè)父子外邊距合并的范例 概念:在CSS當(dāng)...
    饑人谷_任磊閱讀 665評(píng)論 0 3
  • 在什么場(chǎng)景下會(huì)出現(xiàn)外邊距合并?如何合并?如何不讓相鄰元素外邊距合并?給個(gè)父子外邊距合并的范例 在CSS當(dāng)中,相鄰的...
    Nicklzy閱讀 859評(píng)論 0 49