細說 CSS margin

做者:https://coding.net/u/zhengkenghong
原文:https://blog.coding.net/blog/css-margincss

本文着重描述關於 margin,咱們平常不太容易發現的「坑」。html

盒模型

接觸過 CSS 的人應該都知道 CSS 的盒模型segmentfault

盒模型

由內容邊緣(Content edge)包圍造成的是內容盒(Content Box),類推還有內邊距盒(Padding Box)、邊框盒(Border Box)、外邊距盒(Margin Box)。
其中內容盒、內邊距盒、邊框盒的背景由background屬性決定,而外邊距盒的背景是透明的。學習

CSS margin 屬性

關於 margin 屬性,有幾點可能跟咱們的直覺不相符:spa

  • 若是 margin 的值是百分比,則是相對於父元素的內容盒寬度來計算的,即便 margin-top 和 margin-bottom 也是如此。所以即便父元素的高寬不相等,子元素的 margin 元素指定了相同的百分比值,則子元素各個方向的 margin 計算值都是相等的。.net

  • margin-top 和 margin-bottom 值對行內非替換元素(non-replaced inline element)是無效的。所以咱們能夠指定 img 元素的 margin-top 和 margin-bottom,而非替換行內元素(如 i,span 等)設置 margin-top 和 margin-bottom 卻不會產生效果。翻譯

相鄰的 margin(Adjoining margin)

若是兩個垂直方向上的 margin,它們中間沒有其餘垂直 margin,但它們之間不必定相接觸,咱們就說這兩個 margin 是垂直毗連(vertical-adjacent)的,包括如下四種狀況,知足其中之一便可:設計

  • 父元素的 top margin 和第一個子元素的 top margin3d

  • 父元素的bottom margin 和最後一個子元素的 bottom margincode

  • 元素的 bottom margin 和與這個元素相鄰的兄弟元素的 top margin

  • 若是一個元素,它沒有生成 BFC、沒有包含正常流的子元素、min-height是0、height是0或者 auto,則它的 top margin 和 bottom margin 也是垂直毗連的

若是兩個 margin 知足如下三個條件,咱們就說這兩個 margin 是相鄰(adjoining)的:

  1. 這兩個 margin 是垂直毗連的,即知足上面四種狀況之一

  2. margin 的兩個元素都是正常流的塊級元素,而且在同一個 BFC

  3. 兩個 margin 之間沒有行盒(line box)、清除浮動後的空隙(clearance)、padding和 border

margin 摺疊(Collapsing margins)

margin 摺疊,即相鄰的 margin 有可能會被摺疊成一個。

好比元素 #a 指定了 margin-bottom 爲 10px,而它下方的元素 #b 指定了 margin-top 爲 20px,如這樣

圖片

元素 #a 的 margin-bottom 和元素 #b 的margin-top 在位置上重疊了,它們之間的距離是 20px,即元素 #b 的 bottom margin 長度,這就是 margin 摺疊現象。關於這個現象,能夠這麼理解:

margin 定義的是它與其餘盒子之間的最小間距。其中元素 #a 指定了 margin-bottom 爲 10px,就代表它下方的元素 #b 與它至少要有 10px 的距離,它指定的是一個最小值,所以實際的距離能夠比這個大。

元素 #a 下方的元素 #b 也設置了 margin-top 爲 20px,若是不折疊,則他們之間就有 30px 的距離。若是摺疊成了一個 20px 的距離,則對元素 #a 來講,它的 margin-bottom 要求至少要有 10px 的距離,是知足的,而對於元素 #b 來講,它的 margin-top 要求至少要有 20px 的距離,也是知足的。

而 margin 摺疊的存在,實際上是爲了能夠在視覺上顯得更美觀,也更貼近設計師的預期。

margin 摺疊規則

並非全部的 margin 均可以摺疊,須要知足如下條件:

  • 垂直相鄰的 margin 纔有可能摺疊,水平 margin 永遠不折疊

  • 根元素(即 html 元素)的 margin 永遠不折疊

  • 若是一個元素,它的 top margin 和 bottom margin 是相鄰的,而且有清除浮動後的空隙(clearance),這個元素的 margin 能夠跟兄弟元素的 margin 摺疊,可是摺疊後的 margin 不能跟父元素的 bottom margin 摺疊。

須要注意的是,margin 並非只能摺疊一次,多個知足要求的 margin 均可以進行摺疊造成一個摺疊後的 margin(collapsed margin)。
而且假如這個摺疊後的 margin 是由 margin A 等摺疊而來的,若是有 margin X 跟 margin A 是相鄰的,則咱們也認爲 margin X 跟這個摺疊後的 margin 相鄰

摺疊後的 margin 大小

當兩個或者兩個以上的 margin 摺疊後,margin 的值計算以下:

  • 若是 margin 都是正數,則取他們當中的最大值

  • 若是 margin 中有正有負,則取最大的正數加上最小的負數(如最大的 margin 是 20px,最小的 margin 是 -20px,則他們計算後的值是 0)

  • 若是 margin 中都是負數,則取他們當中的最小值

幾道思考題

浮動、定位元素的 margin 不會和其餘任何元素的 margin 發生重疊,包括它的子元素。

這是由於浮動元素脫離了正常流,因此它和其餘相鄰元素就不處與同一個流中,天然不相鄰;又由於浮動元素的內容盒會造成一個新的 BFC,因此浮動元素跟子元素不處與同一個 BFC 中,所以它們的 margin 也不能摺疊。定位元素同理可得。

inline-block 的元素不會和其餘元素的 margin 發生摺疊,包括它的子元素。

由於 margin 摺疊只會發生在塊級元素上,所以 inline-block 元素的 margin 不會和兄弟元素摺疊,又由於 inline-block 的內容盒會造成一個新的 BFC,因此 inline-block 元素自己也不會和子元素的 margin 發生摺疊

margin 摺疊的幾個栗子

栗子1

若是兩個 margin 知足如下三個條件,咱們就說這兩個 margin 是相鄰(adjoining)的:

  • 兩個 margin 之間沒有行盒(line box)、清除浮動後的空隙(clearance)、padding和邊框

針對這個條件,咱們經過增長 padding 的方式來阻止 margin 的摺疊:

圖片

若是 #container 沒有下邊框,則 #container 的 bottom margin 和 #inner 的 bottom margin 是相鄰的,所以它們摺疊了,而且 #inner 撐開了 #container 元素,因此能夠看到 #container 元素的高度變成了 10px,且顯示的是 #inner 的紅色背景

圖片

當給 #container 添加一個下邊框,兩個 margin 之間就邊框的阻隔,他們就不相鄰了,所以不能摺疊。因此能夠看到 #container 被撐開成了 20px,其中 10px 是 #inner 的高度,還有 10px 是 #inner 的 bottom margin,而且因爲 margin 是透明的,所以 #container 露出了部分藍色的背景。

栗子2

若是兩個 margin 知足如下三個條件,咱們就說這兩個 margin 是相鄰(adjoining)的:

  • margin 的兩個元素都是正常流的塊級元素,而且在同一個 BFC

咱們經過建立新的 BFC來阻止 margin 的摺疊:

圖片
圖片

如上圖 #container 元素和 #inner 元素同屬於一個 BFC 中,#container 的 top margin 和 #inner 的 top margin 摺疊,bottom margin 同理。
但若是讓 #container 跟 #innter 處在不一樣的 BFC 中,則 top margin 和 bottom margin 都不會摺疊,如:

圖片

給 #container 元素增長一個 overflow: hidden 屬性,讓它的內容盒生成一個獨立的 BFC,而 #inner 處於這個獨立的 BFC 中,所以 #container 和 #inner 就處於兩個不一樣的 BFC 中了,因此他們的 margin 不能摺疊。

栗子3

  • 若是一個元素,它自己的 top margin 和 bottom margin 是相鄰的,而且有清除浮動後的空隙(clearance),這個元素的 margin 能夠跟兄弟元素的 margin 摺疊,可是摺疊後的 margin 不能跟父元素的 bottom margin 摺疊。

圖片

給父元素 #container 設置了一個灰色背景,而且沒有設置高度,所以高度會隨着內容而擴展,margin 設置爲 50px。
其中有一個紅色的浮動元素 #floated,高寬都設置爲 40px。
給 #cleared 設置了 15px 的 margin,而且元素的高度、padding、margin 都爲0,所以 #cleared 元素的 top margin 和 bottom margin 是相鄰的。這個元素的位置以下圖所示:

圖片

由於 #cleared 元素清除了左浮動,因此 #cleared 元素下移。
而 #cleared 元素和 #slibling 元素的 margin 摺疊了,所以能夠看到他們的位置是重疊的。

圖片

因爲這條規則的存在,致使他們摺疊後的 margin 不能跟 #container 的 bottom margin 進行摺疊,所以 #container 的高度被撐開。

若是沒有這條規則,他們還應該跟 #container 的 bottom margin 進行摺疊,如:

圖片

以上這張圖,在去掉了 #cleared 元素的 clear 屬性以後,就不知足這條規則了,因此能夠看到 #container 的高度就只有 40px,即紅色的浮動元素的高度,而 #cleared 元素、#sibling 元素、#container 元素的 margin 都摺疊成了一個。

結語

這篇文章的絕大多數內容都是從官方規範翻譯而來,同時也參考也網上一些寫的比較好的文章而寫的一個介紹性文章,其中有部份內容並無展開,如 BFCclearance 等,由於這部份內容不是三言兩語就能夠解釋清楚,我本人也須要進行更深刻的學習理解,因此請讀者自行查閱相關文章

參考文獻

https://www.w3.org/TR/CSS2/box.html
https://www.w3.org/TR/CSS2/visuren.html
http://www.w3cplus.com/css/understanding-bfc-and-margin-collapse.html
http://www.javashuo.com/article/p-bcrraqtw-ca.html
http://www.javashuo.com/article/p-gpwjxyjs-e.html
http://melonh.com/css/2015/04/28/understand-margin-collapse.html

相關文章
相關標籤/搜索