關於 CSS 邊距你需要知道的一切
已發表: 2022-03-10 當我們學習 CSS 時,我們大多數人學到的第一件事就是 CSS 中盒子的各個部分的細節,被描述為 CSS 盒子模型。 盒子模型中的一個元素是邊距,一個盒子周圍的透明區域,它將其他元素推離盒子內容。 在 CSS1 中, margin-top
、 margin-right
、 margin-bottom
和margin-left
屬性以及同時設置所有四個屬性的簡寫margin
被描述了。
邊距似乎是一件相當簡單的事情,但是,在本文中,我們將看看一些在使用邊距方面會絆倒人們的事情。 特別是,我們將研究邊距如何相互作用,以及邊距折疊實際上是如何工作的。
CSS 盒子模型
與所有關於 CSS 盒子模型部分的文章一樣,我們應該定義我們所說的意思,以及模型是如何通過 CSS 版本來闡明的。 盒子模型是指盒子的各個部分——內容、內邊距、邊框和邊距——是如何佈置和相互作用的。 在 CSS1 中,Box Model 使用下圖所示的 ASCII 藝術圖進行了詳細說明。

盒子每一邊的四個邊距屬性和margin
簡寫都是在 CSS1 中定義的。
CSS2.1 規範有一個插圖來演示盒子模型,還定義了我們仍然用來描述各種盒子的術語。 該規範描述了content box
、 padding box
、 border box
和margin box
,每個都分別由內容的邊緣、填充、邊框和邊距定義。

現在有一個 3 級盒子模型規範作為工作草案。 該規範引用 CSS2 來定義盒子模型和邊距,因此我們將在本文的大部分內容中使用 CSS2 定義。
邊距崩潰
CSS1 規範定義了邊距,也定義了垂直邊距折疊。 從那時起,這種崩潰行為一直是與利潤相關的挫敗感的根源。 如果您考慮到在早期,CSS 被用作文檔格式語言,那麼邊距折疊是有道理的。 邊距折疊意味著當具有下邊距的標題後面跟著具有上邊距的段落時,這些項目之間不會有很大的差距。
當邊距折疊時,它們將合併,以便兩個元素之間的空間成為兩個邊距中較大的一個。 較小的邊距基本上在較大的邊距內結束。
保證金在以下情況下崩潰:
- 相鄰的兄弟姐妹
- 完全空的盒子
- 父元素和第一個或最後一個子元素
讓我們依次看看這些場景中的每一個,然後再看看在這些場景中防止邊距崩潰的因素。
相鄰的兄弟姐妹
我對邊距折疊的最初描述是展示相鄰兄弟之間的邊距是如何折疊的。 除了下面提到的情況,如果你有兩個元素在正常流程中一個接一個地顯示,第一個元素的下邊距將與後面元素的上邊距一起折疊。
在下面的 CodePen 示例中,有三個div
元素。 第一個具有 50 像素的頂部和底部邊距。 第二個的頂部和底部邊距為 20 像素。 第三個的頂部和底部邊距為 3em。 前兩個元素之間的邊距為 50 像素,因為較小的上邊距與較大的下邊距相結合。 3em 中後兩個元素之間的邊距,因為 3em 大於第二個元素底部的 20 個像素。
請參閱 Rachel Andrew 的 Pen [邊距:相鄰兄弟姐妹](https://codepen.io/rachelandrew/pen/OevMPo)。
完全空的盒子
如果一個盒子是空的,那麼它的頂部和底部邊距可能會相互折疊。 在下面的 CodePen 示例中,具有 empty 類的元素的頂部和底部邊距為 50 像素,但是,第一項和第三項之間的間距不是 100 像素,而是 50。這是由於兩個邊距折疊造成的。 向該框添加任何內容(甚至填充)將導致使用頂部和底部邊距並且不會折疊。
請參閱 Rachel Andrew 的 Pen [邊距:空框](https://codepen.io/rachelandrew/pen/JQLGMr)。
父元素和第一個或最後一個子元素
這是最常引起人們注意的邊緣崩潰場景,因為它看起來並不特別直觀。 在下面的 CodePen 中,我有一個帶有類包裝器的div
,並且我為該div
提供了一個紅色的outline
,以便您可以看到它在哪裡。 三個子元素都有 50 像素的邊距。 但是,第一個和最後一個項目與包裝的邊緣齊平; 元素和包裝器之間沒有 50 像素的邊距。
請參閱 Rachel Andrew 的 Pen [邊距:第一個和最後一個孩子的邊距](https://codepen.io/rachelandrew/pen/BgrKGp)。
這是因為子級的邊距與父級的任何邊距一起折疊,因此最終位於父級的外部。 如果您使用 DevTools 檢查第一個孩子,您可以看到這一點。 突出顯示的黃色區域是邊距。

僅塊邊距折疊
最後一個示例還強調了有關邊距折疊的一些內容。 在 CSS2 中,只有垂直邊距被指定為折疊 - 如果您處於水平書寫模式,則即元素的頂部和底部邊距。 所以上面的左右邊距不會折疊並最終在包裝器之外。
注意:值得記住的是,邊距僅在塊方向上折疊,例如在段落之間。
防止保證金崩潰的事情
如果項目具有絕對定位或浮動,則邊距永遠不會折疊。 但是,假設您遇到了上述邊距崩潰的地方之一,您如何阻止這些邊距崩潰?
停止崩潰的第一件事是有問題的元素之間存在某些東西的情況。
例如,一個完全沒有內容的盒子不會折疊它的頂部和底部邊距,如果它有一個邊框,或者應用了填充。 在下面的示例中,我在框中添加了 1px 的填充。 現在在框的上方和下方都有 50 像素的邊距。
請參閱 Rachel Andrew 的 Pen [邊距:帶填充的空框不會折疊](https://codepen.io/rachelandrew/pen/gNeMpg)。

這背後有邏輯,如果盒子完全是空的,沒有邊框或填充,它本質上是不可見的。 它可能是您的 CMS 放入標記中的空段落元素。 如果您的 CMS 添加了多餘的段落元素,您可能不希望它們在其他段落之間造成較大的間隙,因為它們的邊距得到尊重。 在盒子裡添加任何東西,你就會得到這些空白。
在第一個或最後一個通過父級折疊的子級上的邊距可以看到類似的行為。 如果我們給父級添加邊框,子級的邊距會留在裡面。
請參閱 Rachel Andrew 的 Pen [邊距:如果父級有邊框,則第一個和最後一個子級的邊距不會折疊](https://codepen.io/rachelandrew/pen/vqRKKX)。
再一次,這種行為有一些邏輯。 如果出於語義目的而使用包裝元素而不會在視覺上顯示,那麼您可能不希望它們在顯示中引入大的間隙。 當網絡主要是文本時,這很有意義。 當我們使用元素來佈局設計時,它作為行為不太有用。
創建塊格式化上下文
新的塊格式上下文 (BFC) 還將防止邊距通過包含元素折疊。 如果我們再看一下第一個和最後一個孩子的例子,它們的邊距在包裝器之外,並給包裝器display: flow-root
,從而創建一個新的 BFC,邊距留在裡面。
請參閱 Rachel Andrew 的 Pen [Margins: a new Block Formatting Context contains margins](https://codepen.io/rachelandrew/pen/VJXjEp)。
要了解有關display: flow-root
更多信息,請閱讀我的文章“了解 CSS 佈局和塊格式化上下文”。 將overflow
屬性的值更改為auto
將產生相同的效果,因為這也會創建一個新的 BFC,儘管它也可能會創建在某些情況下您不想要的滾動條。
Flex 和網格容器
Flex 和 Grid 容器為它們的子容器建立 Flex 和 Grid格式化上下文,因此它們具有不同的行為來阻止佈局。 這些差異之一是邊距不會崩潰:
“一個 flex 容器為其內容建立了一個新的 flex 格式化上下文。 這與建立塊格式化上下文相同,只是使用 flex 佈局而不是塊佈局。 例如,浮動不會侵入 flex 容器,並且 flex 容器的邊距不會與其內容的邊距一起折疊。”
— 彈性盒 1 級
如果我們採用上面的示例並將包裝器製作成一個 flex 容器,使用flex-direction: column
顯示項目,您可以看到邊距現在包含在包裝器中。 此外,相鄰彈性項目之間的邊距不會相互折疊,所以我們最終在彈性項目之間有 100 像素,項目頂部和底部的總和為 50 像素。
請參閱 Rachel Andrew 的 Pen [邊距:彈性項目的邊距不會折疊](https://codepen.io/rachelandrew/pen/mZxreL)。
您網站的保證金策略
由於邊距崩潰,最好採用一致的方式來處理站點中的邊距。 最簡單的做法是只在元素的頂部或底部定義邊距。 這樣,您不應該經常遇到邊距折疊問題,因為有邊距的一側總是與沒有邊距的一側相鄰。
注意: Harry Roberts 有一篇很棒的帖子詳細說明了為什麼只在一個方向上設置邊距是一個好主意,而不僅僅是因為解決了折疊邊距問題。
此解決方案無法解決您可能遇到的問題,即孩子的邊距通過其父級崩潰。 這個特定問題往往不太常見,了解它發生的原因可以幫助您找到解決方案。 一個理想的解決方案是給需要它的組件display: flow-root
,作為舊瀏覽器的後備,您可以使用overflow
來創建 BFC,將父級轉換為 flex 容器,甚至引入單個像素的填充。 不要忘記您可以使用功能查詢來檢測對display: flow-root
支持,因此只有舊瀏覽器才能獲得不太理想的修復。
大多數時候,我發現知道為什麼利潤率會下降(或沒有下降)是關鍵。 然後,您可以根據具體情況確定如何處理它。 無論您選擇什麼,請務必與您的團隊分享該信息。 很多時候margin collapsing有點神秘,所以採取措施應對它的原因可能並不明顯! 代碼中的註釋對您有很大幫助——您甚至可以鏈接到本文並幫助分享頁邊距折疊知識。
我想我會用其他一些與邊距相關的信息來總結這篇文章。
百分比保證金
當你在 CSS 中使用百分比時,它必須是某物的百分比。 使用百分比設置的邊距(和填充)將始終是父級的內聯大小(水平書寫模式下的寬度)的百分比。 這意味著當使用百分比時,您將在元素周圍有相同大小的填充。
在下面的 CodePen 示例中,我有一個 200 像素寬的包裝器,裡面是一個具有 10% 邊距的框,邊距是 20 像素,即 200 的 10%。
請參閱 Rachel Andrew 的 Pen [邊距:百分比邊距](https://codepen.io/rachelandrew/pen/orqzrP)。
流動相關世界中的邊距
我們在整篇文章中一直在討論垂直邊距,然而,現代 CSS 傾向於以相對流動的方式而不是物理方式來考慮事物。 因此,當我們談論垂直邊距時,我們實際上是在談論塊維度中的邊距。 如果我們處於水平書寫模式,這些邊距將是頂部和底部,但在從左到右書寫的垂直書寫模式中,這些邊距將是左右。
一旦使用邏輯的、流動的相對方向,談論塊開始和塊結束就變得更容易了,而不是頂部和底部。 為了使這更容易,CSS 引入了邏輯屬性和值規範。 這將流相關屬性映射到物理屬性上。
對於邊距,這為我們提供了以下映射(如果我們使用英語或任何其他具有從左到右文本方向的水平書寫模式)。
-
margin-top
=margin-block-start
-
margin-right
=margin-inline-end
-
margin-bottom
=margin-block-end
-
margin-left
=margin-inline-start
我們還有兩個新的速記,允許同時設置兩個塊或同時設置兩個塊。
-
margin-block
-
margin-inline
在下一個 CodePen 示例中,我使用了這些流相關關鍵字,然後更改了框的書寫模式,您可以看到邊距如何跟隨文本方向,而不是與物理上、右、下和左綁定。
請參閱 Rachel Andrew 的 Pen [Margins: flow relative margins](https://codepen.io/rachelandrew/pen/BgrQRj)。
您可以在 MDN 上閱讀有關邏輯屬性和值的更多信息,或者在 Smashing Magazine 上的文章“了解邏輯屬性和值”中閱讀。
總結
您現在知道關於利潤的大部分知識了! 簡而言之:
- 保證金崩潰是一回事。 了解它為什麼會發生以及何時不會發生將幫助您解決它可能導致的任何問題。
- 在一個方向上設置邊距只能解決許多與邊距相關的難題。
- 與 CSS 中的任何內容一樣,與您的團隊分享您所做的決定,並評論您的代碼。
- 隨著網絡朝著與書寫模式無關的方向發展,考慮塊和內聯尺寸而不是物理頂部、右側、底部和左側將有助於您。