你怎麼看CSS中的margin合併?

什麼是margin合併?

塊級元素的上外邊距(通常)與下外邊距有時會合併爲單個外邊距,這樣的現象稱爲“margin合併”。

從定義上,我們可以看出如下兩點:

  1. 塊級元素。但不包括浮動(float)和絕對定位(relative)元素——儘管這二者可以使元素塊級化;
  2. 只發生在垂直方向。嚴格來說,應該是【只發生在和當前文檔流方向的相垂直方向上】——但是默認文檔流是水平的

這一幕,讓我不禁想起了BFC:有記爲證
傳說,只要滿足了以下任一要求即可觸發BFC:

  • body 根元素
  • 浮動元素:float 除 none 以外的值
  • 絕對、固定定位元素:position (absolute、fixed)
  • display 爲 inline-block、table-cells、flex
  • overflow 除了 visible 以外的值 (hidden、auto、scroll)

可見,BFC和margin合併之間有些相似:作用於“流”、對元素與“其他元素”位置有影響

fg

margin合併也有三大場景

(1)相鄰兄弟元素的margin合併

//css
p {margin: 1rem 0;}
//html
<p>第一行</p>
<p>第二行</p>

則第一行和第二行之間的間距仍爲1rem。

(2)父級和第一個/最後一個子元素
事實上,在開發中大多時候你都要忍受這種父子margin合併帶來的麻煩。
比如你在官網有一張大圖(二維碼),配合有一個很大的網站標題。由於這個標題一般在頭圖中間的某位置,因此,我們很自然會想到使用margin-top定位。
然後問題就來了:頭圖居然“掉”了下來!
當時筆者遇到這個問題時查閱資料後才發現這就是父子margin合併。這裏大家需要清楚這個“合併”的概念:父元素沒有出一點力,而子元素出了全部的力,最終margin卻全部合到了父元素上。簡單點說就是雖然是在子元素上設置的margin-top,但實際上等於是作用在父元素上。

但這裏需要注意一點:“等於”並不是“就是”。
我們如果使用getComputedStyle方法獲取父元素的margin-top值,還是在css中設置的值,而並非margin合併後的“表現值”。

那如何阻止這裏的margin合併呢?
同樣的,只要滿足了以下任一要求即可觸發:

  • 父元素設置爲塊狀格式化上下文
  • 父元素設置border-top(bottom)值
  • 父元素設置padding-top(bottom)值
  • 父元素和第一個(最後一個)子元素之間添加內聯元素進行分隔
  • 父元素設置height、min-height或max-height(此針對margin-bottom合併)

所以,對於上面所說的問題,只要在父元素中添加:

//css
xxx {overflow: hidden;}

即可(其原理就是overflow屬性爲父級元素塊狀格式化上下文)
【…其實筆者覺得這裏還是“套用的”BFC,爲所有子元素套上一個容器,避免對外面(其它)元素產生干擾】

說到這裏,筆者不得不提一句:jQuery中有個$().slideUp() / $().slideDown()方法,如果在使用此動畫時,發現這塊內容在動畫開始或結束時會跳那麼一下,那基本上可以說是佈局存在margin合併了。不過這個更確切的說是“margin合併被阻止”導致的。其解決辦法就是在使用時對元素設置overflow: none;即可

(3)空塊級元素的margin合併

//css
.father {overflow: hidden;}
.son {margin: 1rem 0;}
//html
<div class="father">
	<div class="son"></div>
</div>

此時,父級元素這個div自身高度僅1rem,因爲子級元素的margin-top和margin-bottom合併在一起了撐開的距離。

其實,像文前面所說的,就算兄弟不相鄰,合併也是可以發生的——但前提是中間“插手”的也是個會合並的傢伙:比如div;所以,這裏空塊級元素的margin合併特性即使自身沒有設置margin也是會發生的。

解決:

  • 設置垂直方向的border
  • 設置垂直方向的padding
  • 裏面添加內聯元素(直接物理Space是沒用的…)
  • 設置height或者min-height

fg

margin合併的計算規則?

我們可以把margin的合併規則總結爲“正正取大值”、“正負值相加”、“負負取最負”三句“祕訣”。
——不管是對元素自身,還是對兩個元素之間關係,都適用。

fg

margin合併的意義

margin(尤其是margin-top)這樣設計,其實在實際內容呈現中有重要意義——但由於其“怪異”,導致現在廣爲流傳的說法:“margin-top合併bug”,這其實是不正確的。

<h2>文章標題</h2>
<p>段落1</p>
<p>段落2</p>
<ul>
	<li>列表1</li>
	<li>列表2</li>
</ul>

這裏的h2、p、ul標籤默認全部是有垂直方向的margin值的,而且單位全部都是em。

首先解釋-下爲何 需要 margin 值。其實原因很簡單, css世界的設計本意就是圖文信息展示,有了默認的 margin 值,我們的文章、新聞就不會擠在一起,垂直方向就會層次分明、段落有致,閱讀體驗就會好!爲何使用em作爲單位也很好理解,大家應該知道瀏覽器默認的子虧大小是可以自定義的吧(例如,默認的是16 像素),假如我們設置成更大號的字號,同時HTML標籤的margin是像素大小,則會發生文字變大但是間距不變的情況,原本段落有致的閱讀體驗必然又會變得令人室息。em作爲相對單位,則可以讓我們的文章或新聞無論多大的字體都排版良好。可以看到,HTML標籤默認內置的CSS屬性值完全就是爲了更好地進行圖文信息展示而設計的。
我們平時進行網站開發的時候都會重置各種默認的margin尺寸,這是件需要好好審視的事情,對於絕大多數網站,確實需要做這樣的處理,因爲這些網站鮮有傳統的圖文信息展示區域。但是,如果你的站點是博客、新聞門戶或公衆號文章,我們應該做的是統一標籤的margin大小,而不是一股腦地重置成0。

下面說說margin合併的意義。對於兄弟元素的margin合併其作用和em類似,都是讓圖文信息的排版更加舒服自然。假如說沒有margin合併這種說法,那麼連續段落或列表之類首尾項間距會和其他兄弟標籤成1:2關係;文章標題距離頂部會很近,而和下面的文章詳情內容距離又會很開,就會造成內容上下間距不一致的情況。 這些都是糟糕的排版體驗。而合併機制可以保證元素上下間距一致, 無論是h2標題這種margin偏大的元素,還是中規中矩的p元素,因爲“正正取大值”。
父子 margin 合併的意義在於:在頁面中任何地方嵌套或直接放入任何裸div,都不會影響原來的塊狀佈局。div 是網頁佈局中非常常用的一個元素,其語義是沒有語義,也就是不代表任何特定類型的內容,是一- 個通用型的具有流體特性的容器,可以用來分組或分隔。由於其作用就是分組的,因此,從行爲表現上來看,一個純粹的

元素是不能夠也不可以影響原先的佈局的。現在有如下一段HTML:

<div style= "margin-top:20px;"></div>

那麼請問:現在要在上面這段HTML的外面再嵌套一層div元素, 假如說現在沒有父子margin合併,那這層新嵌套的div豈不阻斷了原本的兄弟margin合併?很有可能間距就會變大,妥妥地影響了原來的佈局,這顯然就違背了div的設計作用了。所以纔有了父子margin合併,外面再嵌套一層div元素就跟沒嵌套一樣, 表現爲margin-top:20px就好像是設置在最外面的div元素上-樣。
自身margin合併的意義在於可以避免不小心遺落或者生成的空標籤影響排版和佈局。
例如:

<p>第一行</p>
<p></p>
<p></p>
<p>第二行</p>

其實它和下面這段代碼效果是一樣的:

<p>第一行</p>
<p>第二行</p>

但若是沒有margin合併,上面代碼中間怕是要隔了許多“莫名其妙的”空格了吧。


知道了margin合併的意義和作用,你完全可以在列表中設置保留上下margin:

margin-top: 15px;
margin-bottom: 15px;

而不是隻戰戰兢兢的使用margin-top。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章