尺寸中padding負(fù)責(zé)內(nèi)邊距,一般情況下(拋開上一章的詭異現(xiàn)象)不會(huì)給使用者帶來太多的麻煩,因此作者稱之為溫和的padding,而margin則有些激進(jìn),雖說負(fù)責(zé)外邊距,但有時(shí)候還能做一些"內(nèi)邊距"的事情(負(fù)邊距),還自帶了些特殊屬性(如疊壓),本文會(huì)通過實(shí)例深入探究margin負(fù)邊距的使用以及疊壓問題的產(chǎn)生和計(jì)算方式
1.margin負(fù)邊距的正確打開方式
說到margin,通常我們會(huì)想到一層透明的外邊距,用于劃分元素與元素之間的界限,然而margin除了可以劃分邊界,還可以改變元素"可視尺寸",注意這里我沒有用內(nèi)部尺寸,因?yàn)閙argin和padding在改變元素可視尺寸方面幾乎是互補(bǔ)的。對于設(shè)定了width或者元素保持"包裹性"的時(shí)候,padding會(huì)改變元素的可視尺寸,而margin正好相反,margin只會(huì)在元素“充分利用空間”狀態(tài)的時(shí)候,才能改變元素的可視尺寸 。當(dāng)然這兩個(gè)也不是完全互補(bǔ)的,這里當(dāng)作一個(gè)思考,請自己體會(huì)。
剛才說到了設(shè)定了寬度的元素和保持“包裹性”的元素不能通過margin影響可視尺寸,設(shè)定width這個(gè)很好理解,那么保持包裹性的元素有哪些呢?
這里來列舉一下常見的有:absolute,fixed,float ,inline-box(inline-block, table-cell, table-caption, flex, inline-flex)。
所以我們在遇到上述元素的時(shí)候,就不需要嘗試用margin去改變他的可視區(qū)了,無效。由于我平時(shí)最愛用的inline-block元素也在其中,所以我很少用margin負(fù)邊距去管理元素可視區(qū)。下面,我們來看一個(gè)最簡單的margin負(fù)邊距影響元素可視區(qū)的演示,代碼如下:
<div class="father">
<div class="son"></div>
</div>
<style>
.father{
width: 300px;
height: 200px;
background: #F56C6C;
}
.son{
margin: 0 -20px;
height: 100px;
background: #E6A23C;
}
</style>
由于markdown編輯器支持標(biāo)簽語言,因此我們可以直接預(yù)覽最終效果如下(小提示:你可以通過瀏覽器直接檢查下面的元素看到CSS樣式)

這里有兩個(gè)要注意的點(diǎn),首先son是display默認(rèn)為block的元素,符合充分利用水平空間的規(guī)則,其次son自身不帶width申明,所以width在負(fù)邊距的作用下最終width = father.width + 20*2 ,如上圖所示。
負(fù)邊距除了能夠改變"充分可利用空間"的可視區(qū)域之外,還可以利用其可以改變尺寸的特性,實(shí)現(xiàn)一些特殊的布局效果。如作者給出的例子如下:
<div class="box box-right-same">
<div class="full">
<p>DOM文檔流中,圖片定寬在右側(cè),視覺呈現(xiàn)也在右側(cè),順便表現(xiàn)此時(shí)一致。</p>
</div>
<img src="1.jpg" class="img">
</div>
<style>
/* 右浮動(dòng),圖片DOM在后,和視覺表現(xiàn)一致 */
.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>
結(jié)果如下圖所示:

結(jié)果如上圖,在本例中,由于full的寬度是100%,且他和img元素均為浮動(dòng)元素,因此img元素在沒有設(shè)置margin之前應(yīng)當(dāng)流到full元素下面,而加了負(fù)邊距之后,img元素的寬度增加了128px,正好等于圖片的寬度,此時(shí)圖片元素全部跑到了增加的負(fù)邊距中去,導(dǎo)致img元素本身變成了"0寬度",于是0寬度元素就浮上來了,因?yàn)樗安恍枰闭紦?jù)寬度,他的寬度由負(fù)邊距提供了。(這一段測試個(gè)人持保留意見,有不同觀點(diǎn)的可在下方留言)
2.深入探究margin合并的三個(gè)條件
塊級(jí)元素的上外邊距(margin-top)和下外邊距(margin-bottom)有時(shí)會(huì)發(fā)生“重疊”,這樣的現(xiàn)象叫做margin合并。從定義上來看,可以確認(rèn)兩個(gè)信息。
(1)塊級(jí)元素
(2)垂直方向。不考慮writing-mode的情況下,文檔流默認(rèn)為水平方向,因此這里的垂直方向是指垂直于文檔流的方向,而不是簡單的上下左右。
margin合并一般有三種場景
(1)相鄰兄弟元素margin合并。
(2)父級(jí)和第一個(gè)/最后一個(gè)子元素。(作者的這個(gè)表達(dá)可能有一些問題,需要配合第三點(diǎn)來看)
(3)空塊級(jí)元素的margin合并。
下面我將列舉一些場景,來探究一下每個(gè)場景的哪些margin發(fā)生了疊壓。
<p>一段話</p>
<p>一段話</p>
<p>一段話</p>
<p>一段話</p>
<style>
p{
margin: 1em 0;
}
</style>
結(jié)果如下圖所示:

這個(gè)例子中,顯然是相鄰兄弟元素的margin合并,可以看到p標(biāo)簽的上下外邊距是1em,但每兩行之間的的距離并不是1+1=2,而是1和1疊壓之后 = 1。下面來看第二個(gè)場景。
<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>
結(jié)果如下圖所示:

在本例中,父元素高度400,子元素高度200,上外邊距200,想象之中,子元素應(yīng)該"定位"在父容器底部,但由于父級(jí)和第一個(gè)/最后一個(gè)子元素的margin疊壓(這個(gè)理論是否完全正確我們在后面的例子中證明),子元素的margin-top"借"給了父元素,然后自己的margin-top似乎變成了0,在實(shí)際開發(fā)的時(shí)候,父子元素的margin合并也會(huì)給我們帶來許多麻煩,那么,如何解決這個(gè)煩惱呢?作者提供了幾種方案(滿足任何一種即可),這里我會(huì)有一些自己的觀點(diǎn)在里面。
(1)父元素設(shè)置為塊狀格式化上下文元素(聽不懂沒關(guān)系,overflow:hidden就可以)
(2)父元素設(shè)置border-top/bottom(>0)的值(border-top解決margin-top,border-bottom解決margin-bottom)
(3)父元素設(shè)置padding-top/bottom(>0)的值(同border)
(4)父元素和第一個(gè)子元素之間添加(非空)內(nèi)聯(lián)元素進(jìn)行分隔(針對margin-top)
(5)父元素和最后一個(gè)子元素之間添加(非空)內(nèi)聯(lián)元素進(jìn)行分隔(針對margin-bottom)
(6)父元素設(shè)置height,min-height或max-height(注意本條只對margin-bottom有效)
經(jīng)過本人測試,CSS世界似乎對申明這個(gè)玩意不感冒,作者說設(shè)置border/padding的值即可,我設(shè)置0,"竟然"不行,所以補(bǔ)充了>0的限制條件,但由于這條規(guī)則破壞了容器的大小,所以不推薦這兩種解決方案。
在實(shí)際驗(yàn)證的時(shí)候,第四條/第五條在谷歌瀏覽器中也會(huì)由于“0”值不生效,因此我把它劃掉了,因?yàn)檫@個(gè)解決方案實(shí)在是太蠢了,你必須要在內(nèi)聯(lián)元素里面寫點(diǎn)什么才能解決margin疊壓問題,這可比破壞容器大小嚴(yán)重多了,直接就影響文本顯示了。因此最佳的解決方案就是第一條,父元素設(shè)置為塊狀格式化上下文元素,雖然我并不知道這個(gè)是什么意思。
進(jìn)入今天的重頭戲,我覺得作者寫的有問題的一個(gè)點(diǎn),我們先在剛才父子疊壓代碼的基礎(chǔ)上添加一個(gè)空塊級(jí)標(biāo)簽。
<div class="father">
<!-- 我是一個(gè)空塊級(jí)元素 -->
<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>
此時(shí)你會(huì)發(fā)現(xiàn)頁面無任何變化,其實(shí)這里涉及到兩個(gè)知識(shí)點(diǎn),首先是空塊級(jí)標(biāo)簽的margin疊壓,由于其本身沒有任何寬高,也沒有margin值,因此他只會(huì)和相鄰的son元素進(jìn)行疊壓,空div的margin-bottom:0 和 son元素的margin-top:200合并之后,可以認(rèn)為空div的margin-bottom變成200了,此時(shí),空塊級(jí)元素的margin-bottom:200又和自身的margin-top:0合并,使得自身的margin-top也受到了感染,最后疊壓成垂直margin=200的空塊級(jí)元素,這時(shí)候父元素感應(yīng)到他的第一個(gè)子元素有margin-top,就和他進(jìn)行了一波margin-top疊壓,所以最終的表現(xiàn)和第二個(gè)例子的結(jié)果相同。
測試到這里,我還覺得沒什么問題,然后我又想到剛才內(nèi)聯(lián)空標(biāo)簽對第二個(gè)例子也不會(huì)有任何影響,那么問題來了,父級(jí)和第一個(gè)/最后一個(gè)子元素的疊壓這句話究竟是什么意思?驚覺這句父元素和第一個(gè)子元素之間添加內(nèi)聯(lián)元素進(jìn)行分隔似乎還有什么別的意思,我個(gè)人猜測作者是想表達(dá)內(nèi)聯(lián)元素破壞了發(fā)生疊壓的三個(gè)規(guī)則,因?yàn)閮?nèi)聯(lián)元素不會(huì)發(fā)生margin疊壓,因此可以用這個(gè)進(jìn)行分割(即使該內(nèi)聯(lián)元素為空,當(dāng)然實(shí)際測試中為空是沒有任何效果的)。根據(jù)測試結(jié)果,我提出的一個(gè)大膽的假設(shè):margin疊壓,會(huì)直接忽略掉所有空標(biāo)簽(當(dāng)然空標(biāo)簽不能有什么奇奇怪怪的樣式)。這么一來,內(nèi)聯(lián)空標(biāo)簽的問題就解決了。當(dāng)然這個(gè)假設(shè)還有待驗(yàn)證,去作者提供的論壇碰碰運(yùn)氣。
3.margin合并的計(jì)算規(guī)則
關(guān)于margin合并的計(jì)算規(guī)則,我個(gè)人傾向于完全套用作者的三句精辟總結(jié):
點(diǎn)擊領(lǐng)取免費(fèi)資料及課程
- “正正取大值”
- “正負(fù)值相加”
- “負(fù)負(fù)最負(fù)值”
這里我只說明正負(fù)值相加的情況,雖然這東西其實(shí)并沒有什么軟用,看下面的例子
.a{margin-bottom:50px}
.b{margin-top:-20px}
<div class="a"></div>
<div class="b"></div>
復(fù)制代碼
此時(shí)a和b的間距=-20+50 = 30px
4.深入理解margin:auto
總是喜歡以深入命名,其實(shí)就是一個(gè)??測試,根據(jù)瀏覽器的表現(xiàn)結(jié)果,來猜測原理,在理解margin:auto之前,先來看下面這個(gè)例子。
<div class="father">
<div class="son"></div>
</div>
<style>
body{
margin: 0;
}
.father{
height: 400px;
background: yellow;
}
.son{
width: 200px;
margin-right: 100px;
margin-left: auto;
height: 200px;
background: red;
}
</style>
結(jié)果如下圖所示:

可以從結(jié)果中得出如下結(jié)論:
(1)margin:auto是有用的,去掉margin-left:auto后,margin-right失效
(2)margin:auto屬性管理的是容器的剩余空間
何為容器的剩余空間?最尋常的情景就是你在body里添加了一個(gè)寬度

那么問題又出現(xiàn)了,為什么此時(shí)在垂直方向上沒有垂直居中呢?原因在于觸發(fā)margin:auto計(jì)算有一個(gè)前提條件,就是width或height為auto時(shí),元素是具有對應(yīng)方向的自動(dòng)填充屬性的。從本例來看,當(dāng)son的width為auto時(shí),son的寬度為100%,也就是可計(jì)算的剩余空間為100%-width具體值,當(dāng)son的height為auto時(shí),不好意思,son的高度變0了,看都看不見了,你還要居中他干啥。同時(shí)height:auto也不符合自動(dòng)填充特性。
利用margin:auto管理剩余空間的特性,我們除了可以做到元素的水平居中,還可以實(shí)現(xiàn)元素的"右浮動(dòng)"。只需要如下設(shè)置即可.
<div class="father">
<div class="son"></div>
</div>
<style>
body{
margin: 0;
}
.father{
height: 400px;
background: yellow;
}
.son{
width: 200px;
margin-left: auto;
height: 200px;
background: red;
}
</style>
這里我們通過margin-left:auto管理剩余空間,元素就自動(dòng)右對齊了。
點(diǎn)擊領(lǐng)取免費(fèi)資料及課程
除了水平居中和右對齊,margin:auto還可以實(shí)現(xiàn)水平垂直居中。剛才也說到了,要實(shí)現(xiàn)垂直居中,只需要讓元素在垂直方向上也就是height:auto具有自動(dòng)填充屬性即可。那么什么情況下height:auto有自動(dòng)填充屬性呢?有一種情況就可以,絕對定位元素設(shè)置了top和bottom屬性之后,元素垂直方向上就會(huì)自動(dòng)填滿父容器。如下所示
<div class="father">
<div class="son"></div>
</div>
<style>
body{
margin: 0;
}
.father{
height: 400px;
background: yellow;
position: relative;
}
.son{
position: absolute;
width: 200px;
height: 200px;
background: red;
margin: auto;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
</style>

由于設(shè)置了right,left,top,bottom值,子元素在水平垂直方向上都有用自動(dòng)填充屬性,通過margin管理剩余空間,就實(shí)現(xiàn)了垂直和水平方向的填充。
margin部分的內(nèi)容就寫到這。