margin系列之外邊距折疊

2018-02-24 15:22 更新

margin系列之外邊距折疊

原作者:doyoe
原文鏈接:http://blog.doyoe.com/2013/12/04/css/margin%E7%B3%BB%E5%88%97%E4%B9%8B%E5%A4%96%E8%BE%B9%E8%B7%9D%E6%8A%98%E5%8F%A0/

不懷疑你也在工作中遇見過

幾乎可以不用懷疑,前端工作中的你一定遇見過 margin 折疊。

不確定?別著急,你可能寫過這樣的代碼:

CSS:

p{
    margin: 50px;
}

HTML:

<div id="demo">
    <p>我是一個華麗的段落,別看我文字少</p>
    <p>我是另一個華麗的段落</p>
</div>

大家覺得這 2p 之間會發(fā)生點什么?是會合體呢?還是分開?來看看 DEMO1 margin折疊

好吧,它們真的合體了。按照常規(guī)思路,這 2p 之間的空白應該是第一個 pmargin-bottom 50px 加上第二 pmargin-top 50px,即 50 + 50px = 100px ,但結(jié)果總是出乎意料不是么?它們之間只剩下了 50px,這就是 margin折疊。所以任何人遇見過我都不會覺得意外,因為這樣的Code看起來沒有任何問題。

它們之間到底發(fā)生了些什么?

2p 內(nèi)部到底發(fā)生了什么,才會有這樣的表現(xiàn)?

早在CSS1中就有對 margin 折疊的說明,我們來看看:

原文:The width of the margin on non-floating block-level elements specifies the minimum distance to the edges of surrounding boxes. Two or more adjoining vertical margins (i.e., with no border, padding or content between them) are collapsed to use the maximum of the margin values. In most cases, after collapsing the vertical margins the result is visually more pleasing and closer to what the designer expects.

翻譯:外邊距用來指定非浮動元素與其周圍盒子邊緣的最小距離。兩個或兩個以上的相鄰的垂直外邊距會被折疊并使用它們之間最大的那個外邊距值。多數(shù)情況下,折疊垂直外邊距可以在視覺上顯得更美觀,也更貼近設計師的預期。

從這段話中,我們能獲得一些有用的信息:

  • 發(fā)生折疊需要是相鄰的非浮動元素;
  • 折疊發(fā)生在垂直外邊距上,即margin-top/margin-bottom;
  • 折疊后取其中最大的那個margin值作為最終值;

所以 DEMO1 中的 2p 符合折疊的條件,相鄰且都不是浮動元素,于是它們就自然合體了。至于取最大的那個值作為折疊后的最終值,因為都是50px,所以無所謂誰誰誰了。

為什么會有margin折疊這樣的設計?

從上文中,應該能知道個大概?在前面的文章中,我們說過,CSS的基本模型是排版。只是前端工程師現(xiàn)在做得更多的是 布局 而非 文字排版,但CSS并未界定這兩者的區(qū)別。而 margin 折疊是為實現(xiàn)文本排版的段落間距而提供的特性。所以很多時候 margin 折疊的特性會帶給我們諸多疑惑。

再回到 DEMO1 仔細看看,你會驚訝的發(fā)現(xiàn),其實你只要設置每個 p 有相同的垂直外邊距,由于發(fā)生會 margin 折疊,所有的 p 之間,及包含塊與 p 之間的間隙都是相同的,非常美妙且實現(xiàn)簡單,不是么?這正印證了我們引用 CSS1 中的那短話:多數(shù)情況下,折疊垂直外邊距可以在視覺上顯得更美觀,也更貼近設計師的預期。

浮動元素真的不會發(fā)生margin折疊嗎?

質(zhì)疑精神一直都是進步最重要的驅(qū)動力之一,我們?yōu)槭裁床荒苣??改下代碼看看:

CSS:

p{
    float: left;
    margin: 50px;
}

只改CSS代碼,HTML不變。迫不及待的想看到驗證情況,來吧,還等什么。DEMO2 驗證浮動元素是否會發(fā)生margin折疊。

結(jié)果告訴我們,真的沒有折疊,2p 間的間隙足足有 100px

剩下的2點大家就自行驗證吧,相信你能得到滿意的額測試結(jié)果。

僅此而已?

回想一下我們在 margin系列之百分比 文中提到過受書寫模式影響的一些特性,非常不幸,margin 折疊正好是其中之一。

是的,在CSS2及后續(xù)的規(guī)范中,將margin 折疊描述得更為詳盡了。

在水平書寫模式下,發(fā)生 margin 折疊的是垂直方向,即 margin-top/margin-bottom,在垂直書寫模式下,margin 折疊發(fā)生在水平方向上,即 margin-right/margin-left。

現(xiàn)在我們來解釋一下到底什么是margin折疊?

在CSS中,兩個或以上的塊元素(可能是兄弟,也可能不是)之間的相鄰外邊距可以被合并成一個單獨的外邊距。通過此方式合并的外邊距被稱為折疊,且產(chǎn)生的已合并的外邊距被稱為折疊外邊距。

處于同一個塊級上下文中的塊元素,沒有行框、沒有間隙、沒有內(nèi)邊距和邊框隔開它們,這樣的元素垂直邊緣毗鄰,則稱之為相鄰。

什么是垂直邊緣毗鄰?

  • 元素的上外邊距和其屬于常規(guī)流中的第一個孩子的上外邊距。
  • 元素的下外邊距和其屬于常規(guī)流中的下一個兄弟的上外邊距。
  • 屬于常規(guī)流中的最后一個孩子的下外邊距和其父親的下外邊距,如果其父親的高度計算值為 auto。
  • 元素的上、下外邊距,如果該元素沒有建立新的塊級格式上下文,且 min-height 的計算值為零、height 的計算值為零或 auto、且沒有屬于常規(guī)流中的孩子。

說得很清楚了,我想是的。你可能需要注意的是發(fā)生 margin 折疊的元素不一定是兄弟關系,也能是父子或祖先的關系。

如何避免margin折疊?

我想肯定有人要問,那我不想有 margin 折疊的情況發(fā)生,該怎么辦?其實從上面的規(guī)則中,我們已經(jīng)可以抽出避免 margin 折疊的條件來。

  • margin 折疊元素只發(fā)生在塊元素上;
  • 浮動元素不與其他元素 margin 折疊;
  • 定義了屬性overflow且值不為visible(即創(chuàng)建了新的塊級格式化上下文)的塊元素,不與它的子元素發(fā)生margin 折疊;
  • 絕對定位元素的 margin 不與任何 margin 發(fā)生折疊。
  • 特殊:根元素的 margin 不與其它任何 margin 發(fā)生折疊;
  • 如果常規(guī)流中的一個塊元素沒有 border-top、padding-top,且其第一個浮動的塊級子元素沒有間隙,則該元素的上外邊距會與其常規(guī)流中的第一個塊級子元素的上外邊距折疊。
    可能有些繞,我們驗證一下 DEMO3,在其第一個浮動子元素加個全角空格做間隙,來個反證 DEMO4
  • 如果一個元素的 min-height 屬性為0,且沒有上或下邊框以及上或下內(nèi)邊距,且 height 為0或者 auto,且不包含行框,且其屬于常規(guī)流的所有孩子的外邊距都折疊了,則折疊其外邊距。驗證一下 DEMO5

這樣干掉margin折疊

如果不想發(fā)生 margin 折疊,那么你可以根據(jù)上面的規(guī)則得到方法,不是么?我把它改成非塊元素,讓它浮動,讓它絕對定位,讓它 overflow:hidden DEMO6,用邊框隔開它們 DEMO7…隨你怎樣,信手拈來。

今天狀態(tài)不太好,有些地方寫得欠妥,之后可能會修改一下。

BTW: 這篇文章里可能有不少之前文章中沒出現(xiàn)過的名詞,比如:塊級上下文、行框、常規(guī)流,如果你對此不甚了解,可以先找資料看看,我以后會講到。

enjoy it.

可參考:

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號