1. 程式人生 > >CSS進階(5)—— 深入理解margin

CSS進階(5)—— 深入理解margin

  盒尺寸中padding負責內邊距,一般情況下(拋開上一章的詭異現象)不會給使用者帶來太多的麻煩,因此作者稱之為溫和的padding,而margin則有些激進,雖說負責外邊距,但有時候還能做一些"內邊距"的事情(負邊距),還自帶了些特殊屬性(如疊壓),本文會通過例項深入探究margin負邊距的使用以及疊壓問題的產生和計算方式。依舊先放上內容摘要,請按需觀看。

1.margin負邊距的正確開啟方式

2.深入探究margin合併的三個條件(發現書上的內容不全對)

3.margin合併的計算規則

4.深入理解margin:auto,實現上下水平垂直居中

1.margin負邊距的正確開啟方式

  說到margin,通常我們會想到一層透明的外邊距,用於劃分元素與元素之間的界限,然而margin除了可以劃分邊界,還可以改變元素"可視尺寸",注意這裡我沒有用內部尺寸,因為margin和padding在改變元素可視尺寸方面幾乎是互補的。對於設定了width或者元素保持"包裹性"的時候,padding會改變元素的可視尺寸,而margin正好相反,margin只會在元素“充分利用空間”狀態的時候,才能改變元素的可視尺寸。當然這兩個也不是完全互補的,這裡當作一個思考,請自己體會。

  剛才說到了設定了寬度的元素和保持“包裹性”的元素不能通過margin影響可視尺寸,設定width這個很好理解,那麼保持包裹性的元素有哪些呢?

  這裡來列舉一下常見的有:absolute,fixed,float,inline-box(inline-block, table-cell, table-caption, flex, inline-flex)。

  所以我們在遇到上述元素的時候,就不需要嘗試用margin去改變他的可視區了,無效。由於我平時最愛用的inline-block元素也在其中,所以我很少用margin負邊距去管理元素可視區。下面,我們來看一個最簡單的margin負邊距影響元素可視區的演示。

<div class="father">
	<div class="son"></div>
</div>
<style>
	.father{
		width: 300px;
		height: 200px;
		background: red;
	}
	.son{
		margin: 0 -20px;
		height: 100px;
		background: yellow;
	}
</style>

  這裡有兩個要注意的點,首先son是display預設為block的元素,符合充分利用水平空間的規則,其次son自身不帶width申明,所以width在負邊距的作用下最終width = father.width + 20*2,如上圖所示。

  負邊距除了能夠改變"充分可利用空間"的可視區域之外,還可以利用其可以改變尺寸的特性,實現一些特殊的佈局效果。如作者給出的例子如下:

<div class="box box-right-same">
    <div class="full">
        <p>DOM文件流中,圖片定寬在右側,視覺呈現也在右側,順便表現此時一致。</p>
    </div>
    <img src="1.jpg" class="img">
</div>
<style>
/* 右浮動,圖片DOM在後,和視覺表現一致 */
.box-right-same > .full {
    width: 100%;
    float: left;
}
.box-right-same > .full > p {
    margin-right: 140px;
}
.box-right-same > img {
    float: left;
    margin-left: -128px;
}
</style>

  結果如上圖,在本例中,由於full的寬度是100%,且他和img元素均為浮動元素,因此img元素在沒有設定margin之前應當流到full元素下面,而加了負邊距之後,img元素的寬度增加了128px,正好等於圖片的寬度,此時圖片元素全部跑到了增加的負邊距中去,導致img元素本身變成了"0寬度",於是0寬度元素就浮上來了,因為他“不需要”佔據寬度,他的寬度由負邊距提供了。(這一段測試個人持保留意見,有不同觀點的可在下方留言

2.深入探究margin合併的三個條件

  塊級元素的上外邊距(margin-top)和下外邊距(margin-bottom)有時會發生“重疊”,這樣的現象叫做margin合併。從定義上來看,可以確認兩個資訊。

  (1)塊級元素

  (2)垂直方向。不考慮writing-mode的情況下,文件流預設為水平方向,因此這裡的垂直方向是指垂直於文件流的方向,而不是簡單的上下左右。

  margin合併一般有三種場景。

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

  (2)父級和第一個/最後一個子元素。(作者的這個表達可能有一些問題,需要配合第三點來看

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

  下面我將列舉一些場景,來探究一下每個場景的哪些margin發生了疊壓。

<p>一段話</p>
<p>一段話</p>
<p>一段話</p>
<p>一段話</p>
<style>
body{
	margin: 0;
}
p{
	margin: 1em 0;
}
</style>

  

    這個例子中,顯然是相鄰兄弟元素的margin合併,可以看到p標籤的上下外邊距是1em,但每兩行之間的的距離並不是1+1=2,而是1和1疊壓之後 = 1。下面來看第二個場景。

<div class="father">
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	background: green;
	height: 400px;
}
.son{
	margin-top: 200px;
	height: 200px;
	background: red;
}
</style>

  在本例中,父元素高度400,子元素高度200,上外邊距200,想象之中,子元素應該"定位"在父容器底部,但由於父級和第一個/最後一個子元素的margin疊壓(這個理論是否完全正確我們在後面的例子中證明),子元素的margin-top"借"給了父元素,然後自己的margin-top似乎變成了0,在實際開發的時候,父子元素的margin合併也會給我們帶來許多麻煩,那麼,如何解決這個煩惱呢?作者提供了幾種方案(滿足任何一種即可),這裡我會有一些自己的觀點在裡面。

  (1)父元素設定為塊狀格式化上下文元素(聽不懂沒關係,overflow:hidden就可以)

  (2)父元素設定border-top/bottom(>0)的值(border-top解決margin-top,border-bottom解決margin-bottom)

  (3)父元素設定padding-top/bottom(>0)的值(同border)

  (4)父元素和第一個子元素之間新增(非空)內聯元素進行分隔(針對margin-top)

  (5)父元素和最後一個子元素之間新增(非空)內聯元素進行分隔(針對margin-bottom)

  (6)父元素設定height,min-height或max-height(注意本條只對margin-bottom有效)

  經過本人測試,CSS世界似乎對申明這個玩意不感冒,作者說設定border/padding的值即可,我設定0,"竟然"不行,所以補充了>0的限制條件,但由於這條規則破壞了容器的大小,所以不推薦這兩種解決方案。

  在實際驗證的時候,第四條/第五條在谷歌瀏覽器中也會由於“0”值不生效,因此我把它劃掉了,因為這個解決方案實在是太蠢了,你必須要在內聯元素裡面寫點什麼才能解決margin疊壓問題,這可比破壞容器大小嚴重多了,直接就影響文字顯示了。因此最佳的解決方案就是第一條,父元素設定為塊狀格式化上下文元素,雖然我並不知道這個是什麼意思。

  進入今天的重頭戲,我覺得作者寫的有問題的一個點,我們先在剛才父子疊壓程式碼的基礎上新增一個空塊級標籤。

<div class="father">
	<!-- 我是一個空塊級元素 -->
	<div></div>
	<div class="son"></div>
</div>
<style>
body{
	margin: 0;
}
.father{
	background: green;
	height: 400px;
}
.son{
	margin-top: 200px;
	height: 200px;
	background: red;
}
</style>

  此時你會發現頁面無任何變化,其實這裡涉及到兩個知識點,首先是空塊級標籤的margin疊壓,由於其本身沒有任何寬高,也沒有margin值,因此他只會和相鄰的son元素進行疊壓,空div的margin-bottom:0 和 son元素的margin-top:200合併之後,可以認為空div的margin-bottom變成200了,此時,空塊級元素的margin-bottom:200又和自身的margin-top:0合併,使得自身的margin-top也受到了感染,最後疊壓成垂直margin=200的空塊級元素,這時候父元素感應到他的第一個子元素有margin-top,就和他進行了一波margin-top疊壓,所以最終的表現和第二個例子的結果相同。

  測試到這裡,我還覺得沒什麼問題,然後我又想到剛才內聯空標籤對第二個例子也不會有任何影響,那麼問題來了,父級和第一個/最後一個子元素的疊壓這句話究竟是什麼意思?驚覺這句父元素和第一個子元素之間新增內聯元素進行分隔似乎還有什麼別的意思,我個人猜測作者是想表達內聯元素破壞了發生疊壓的三個規則,因為內聯元素不會發生margin疊壓,因此可以用這個進行分割(即使該內聯元素為空,當然實際測試中為空是沒有任何效果的)。根據測試結果,我提出的一個大膽的假設:margin疊壓,會直接忽略掉所有空標籤(當然空標籤不能有什麼奇奇怪怪的樣式)。這麼一來,內聯空標籤的問題就解決了。當然這個假設還有待驗證,去作者提供的論壇碰碰運氣。

3.margin合併的計算規則

  關於margin合併的計算規則,我個人傾向於完全套用作者的三句精闢總結:

  “正正取大值”

  “正負值相加”

  “負負最負值”

這裡我只說明正負值相加的情況,雖然這東西其實並沒有什麼軟用,看下面的例子

.a{margin-bottom:50px}
.b{margin-top:-20px}
<div class="a"></div>
<div class="b"></div>

 此時a和b的間距=-20+50 = 30px

4.深入理解margin:auto

  總是喜歡以深入命名,其實就是一個