細說 CSS margin

作者:[zhengkonghong](https://coding.net/u/zhengkenghong)

本文著重描述關于 margin,我們日常不太容易發現的“坑”。

盒模型

接觸過 CSS 的人應該都知道 CSS 的[盒模型](https://www.w3.org/TR/CSS2/box.html#box-dimensions)


由內容邊緣(Content edge)包圍形成的是內容盒(Content Box),類推還有內邊距盒(Padding Box)、邊框盒(Border Box)、外邊距盒(Margin Box)。

其中內容盒、內邊距盒、邊框盒的背景由background屬性決定,而外邊距盒的背景是透明的。

CSS margin 屬性

關于 margin 屬性,有幾點可能跟我們的直覺不相符:

如果 margin 的值是百分比,則是相對于父元素的內容盒寬度來計算的,即使 margin-top 和 margin-bottom 也是如此。因此即使父元素的高寬不相等,子元素的 margin 元素指定了相同的百分比值,則子元素各個方向的 margin 計算值都是相等的。

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 margin

父元素的bottom margin 和最后一個子元素的 bottom margin

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

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

如果兩個 margin 滿足以下三個條件,我們就說這兩個 margin 是相鄰(adjoining)的:

這兩個 margin 是垂直毗連的,即滿足上面四種情況之一

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

兩個 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

https://segmentfault.com/a/1190000003099116

https://segmentfault.com/a/1190000003096320

http://melonh.com/css/2015/04/28/understand-margin-collapse.html

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

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,776評論 1 92
  • 1.浮動元素有什么特征?對父容器、其他浮動元素、普通元素、文字分別有什么影響? 何謂浮動元素?有什么特征?所謂浮動...
    草鞋弟閱讀 823評論 0 1
  • ?前端面試題匯總 一、HTML和CSS 21 你做的頁面在哪些流覽器測試過?這些瀏覽器的內核分別是什么? ...
    Simon_s閱讀 2,225評論 0 8
  • 建議在PC端閱讀本文面向對象:對標題中的概念基本不了解或僅僅聽說過名字的人。如果已有一定了解請直接拉到最下看推薦閱...
    粉腸w閱讀 599評論 0 0