深入理解css中的margin屬性

廿四橋明月夜發表於2017-10-09

深入理解css中的margin屬性

  之前我一直認為margin屬性是一個非常簡單的屬性,但是最近做專案時遇到了一些問題,才發現margin屬性還是有一些“坑”的,下面我會介紹margin的基本知識以及那些“坑”。這篇博文主要分為以下幾個部分:

  

第一部分:margin--基礎知識

  要介紹margin的基礎知識,我們不可迴避地要談到css盒子模型(Box Model),一般而言,css盒子模型是用來設計和佈局的。它本質上是一個盒子,包括:外邊距(margin)、邊框(border)、內邊距(padding)以及最中間的內容(content)。下圖即為盒子模型(這裡只談W3C規範的標準盒模型,而不談IE5和IE6在怪異模式中使用的非標準的盒子模型):

 

   我們要介紹的margin在最外層,因為margin(外邊距)一定是透明的,所以它可以用來使得不同的盒子之間留有一定的間隙從而達到佈局美觀等效果。從上面的盒子模型中我們可以看到,margin在四周均存在,我們可以使用margin-top、margin-right、margin-bottom、margin-left分別設定這四個方向的margin值。(注:由於這部分知識較為基礎,所以我不再在這部分不做更多介紹)

 

 

 

第二部分:margin--在同級元素(非父子關係)之間應用

  這一部分主要介紹水平方向和豎直方向的外邊距的合併問題。

 (1)水平方向的外邊距合併

     兩個水平方向的盒子相遇,那麼最終兩者之間的距離為左邊盒子的右外邊距和右邊盒子的做外邊距之和。

     例1:

       程式碼如下:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>水平方向的兩個盒子</title>
    <style>
        *{
            margin:0;
            padding:0;
            border:0;
        }
        body{
            font-size: 0;
        }
        .left{
            width: 100px;
            height: 100px;
            background: red;
            display: inline-block;
            margin-right: 50px;
            font-size: 20px;
        }
        .right{
            width: 100px;
            height: 100px;
            background: yellow;
            display: inline-block;
            margin-left: 50px;
            font-size: 20px;
        }
    </style>
</head>
<body>
    <div class="left">寬為100px,右邊距為50px</div>
    <div class="right">寬為100px,左邊距為50px</div>
</body>
</html>

  效果如下:

這時兩者之間的距離剛好為100px。

  補充說明:大家可以看到,為了使得兩個div(塊狀元素)脫離正常的文件流我使用了display:inline-block;屬性,另外,我還把body的font-size設定為0,這樣可以解決inline-block自身的問題(如果不清楚這裡的描述可以看我的博文《理解與應用css中的display屬性》,這篇文章介紹了inline-block存在的問題),否則兩個div的舉例會大於100px。當然使用float也可以使得兩個div出現在同一行中。

 

  (2)豎直方向的外邊距合併

  兩個豎直方向的盒子相遇時,其豎直方向的距離等於上方盒子的下外邊距和下方盒子的上外邊距中較大的一個。

  例2:

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>水平方向的兩個盒子</title>
    <style>
        *{
            margin:0;
            padding:0;
            border:0;
        }
        .top{
            width: 100px;
            height: 100px;
            margin-bottom: 100px;
            background: red;
        }
        .bottom{
            width: 100px;
            height: 100px;
            margin-top: 50px;
            background: green;
        }
    </style>
</head>
<body>
    <div class="top">高為100px,下邊距為100px</div>
    <div class="bottom">高為100px,上邊距為50px</div>
</body>
</html>

  效果如下:

這時我們肉眼都可以觀察出來,兩者豎直方向的舉例大約為100px(實際就是100px)而非100+50=150px;這正是因為兩個豎直方向的盒子相遇時,其豎直方向的距離等於上方盒子的下外邊距和下方盒子的上外邊距中較大的一個。

  

  另外一個有趣的例子就是:假設有一個元素同時設定了margin-top和margin-bottom,但是內容為空,那麼這兩個margin值也會疊加,值為兩者最大的一個,它類似與豎直方向上兩個盒子margin值的疊加。程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>水平方向的兩個盒子</title>
    <style>
        *{
            margin:0;
            padding:0;
        }
 
        .top{
            width: 500px;
            height: 100px;
            background: red;
        }
        .middle{
            margin-top: 100px;
            margin-bottom:50px;
        }
        .footer{
            width: 500px;
            height: 100px;
            background: green;
        }
 
    </style>
</head>
<body>
    <div class="top">上面的div,高100px</div>
    <div class="middle"></div>
    <div class="footer">下面的div,高100px</div>
</body>
</html>

  最終的效果如下:

  我們發現這時在上面的div和在下面的div之間的舉例並不是100+50=150px,而是兩者中的最大者,即100px。

  

  那麼W3C為什麼會設定這樣的標準而不設定和水平方向一樣的標準呢?即margin值的疊加,實際上這也是有一定的道理的。比如我們需要設計一個由若干個段落構成的一個頁面。我們需要設定margin-top和margin-bottom使得第一段和頁面的最上方有一段距離,使得最後一段和最下方有一段距離。下面是不疊加和疊加的效果圖:

我們可以看到左邊的頁面沒有重疊,那麼兩個段落之間的舉例就是最上方的兩倍間距了,而右邊的頁面發生了重疊,則所有的間距都是相等的。或許這就是這樣設定標準的目的吧,誰知道呢?

 

 

第三部分:margin--在父元素和子元素之間應用(重點

    第二部分介紹了同級元素之間使用margin,而這一部分將要介紹最有意思的父元素和子元素之間margin的應用。這一部分,我們同樣從兩個方面來討論。一方面是子元素設定水平方向上的margin值,另一方面是子元素設定豎直方向的margin值。

  (1)在子元素中設定水平方向的margin值

    我們可以設定margin-left來控制子元素的左邊框和父元素的左邊框之間的舉例。

    例3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>margin</title>
    <style>
        *{padding:0; margin:0; border:0;}
        .father{
            width: 500px;
            height: 500px;
            background: red;
        }
        .son{
            width: 100px;
            height: 100px;
            background: green;
            margin-left: 100px;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">寬度為100px,margin-left為100px。</div>
    </div>
</body>
</html>

  我將子元素的margin-left設定為了100px;效果如下:

即子元素的左邊框和父元素的左邊框之間的距離為100px。與在同級元素之間設定margin不同,因為同級元素之間的margin不會考慮到padding,但是在父元素和子元素就不同了,那麼如果父元素中如果有padding,效果會是什麼樣的呢?請看下面一個例子:

  

  例4:

  下面我們在上面例子的基礎上給父元素新增padding值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>margin</title>
    <style>
        *{padding:0; margin:0; border:0;}
        .father{
            width: 500px;
            height: 500px;
            padding:100px;
            background: red;
        }
        .son{
            width: 100px;
            height: 100px;
            background: green;
            margin-left: 100px;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">寬度為100px,margin-left為100px。</div>
    </div>
</body>
</html>

  上面的程式碼給父元素新增了100px的padding值,效果如下:

我們可以看到子元素舉例上方的距離為100px,因為子元素一定是在父元素的content的部分的,這點毫無疑問。

但是經過測量可以發現子元素的左邊框距離父元素的左邊框之間的距離為200px,因為其中還有100px的左padding值,前面的例子因為我沒有設定padding值,所以沒有觀察出來,因此這就說明了在子元素中設定margin-left,其值實際上是子元素的左邊框距離父元素左padding內側的距離。

 

 例5:margin-right的使用和margin-left的使用是相似的,我在這裡只舉一個例子。

  這個例子在子元素中設定了margin-right值,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>margin</title>
    <style>
        *{padding:0; margin:0; border:0;}
        .father{
            width: 500px;
            height: 500px;
            padding:100px;
            background: red;
        }
        .son{
            float: right;
            width: 100px;
            height: 100px;
            background: green;
            margin-right: 100px;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">寬度為100px,margin-right為100px。</div>
    </div>
</body>
</html>

  這個例子與例4的區別僅在與子元素的位置不同。效果如下:

通過這個例子可以說明margin-right的值是子元素的右邊框和父元素的右padding內側的距離。只是前面的幾個例子我沒有使用padding,所以無法觀察出來。

 

 

 

  (2)在子元素中設定豎直方向的margin值

    按照前面的經驗,理論上來說,我們同樣可以通過設定margin-top的值使得子元素的上邊框和父元素的上padding的內側留有一定的距離。那麼我們就試試吧!

  例6:

        

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>margin</title>
    <style>
        *{padding:0; margin:0; border:0;}
        .father{
            width: 500px;
            height: 500px;
            background: red;
        }
        .son{
            width: 100px;
            height: 100px;
            background: green;
            margin-top: 100px;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">高度為100px,margin-top為100px。</div>
    </div>
</body>
</html>

  這個例子我設定了margin-top為100px,效果如下:

這並不是我們想要的效果啊,我們希望子元素的上部距離父元素的上部為100px,可是我們看到的卻是父元素的上部距離瀏覽器頁面的上部有100px的距離,這是為什麼呢?哪裡出現問題了呢?

  

  實際上這是因為當父元素沒有設定padding值以及border值時,出現了一個bug--父元素的上方與子元素的上方完全重合在了一起,無法分開。所以才會導致上述這種父元素和子元素同時向下的情況。

  對於這種問題解決方法有下面幾種:

  • 方法一:給父元素新增padding-top值
  • 方法二:給父元素新增border值
  • 方法三:給父元素新增屬性overflow:hidden;
  • 方法四:給父元素或者子元素宣告浮動float
  • 方法五:使父元素或子元素宣告為絕對定位:position:absolute;
  • 方法六:給父元素新增屬性 overflow:auto; positon:relative;(注:此方法為後續新增,感謝博友@小精靈Pawn提供此方法

  方法一:基於例6,在父元素的css程式碼中新增padding-top:1px;效果如下:

 

方法的唯一缺點就是增加了1px的誤差。

  

  方法二:基於例6,在父元素的css程式碼中新增border-top:1px solid transparent;效果如下:

 

同樣達到了效果, 缺點同方法一。

  

  方法三:基於例6,在父元素的css程式碼中新增overflow:hidden;效果如下:

 

 

同樣達到了效果,並且沒有任何誤差的存在。堪稱perfect!!!!

  

  方法四:給父元素或者子元素宣告float;基於例6,在子元素css程式碼新增float:left;或者在父元素css程式碼新增float:left;均達到效果,這裡不再展示相同的圖片。

    優點:沒有畫素的誤差。   缺點:float有時是不必要的。

 

  方法五:給父元素或者子元素新增position:absolute;屬性。 同樣達到效果。

    優點:同方法四。  且只要我們不使用top和left也不會有任何影響,所以這也是一種不錯的方法。

(說明:博友 @laden666666 指出,上述方法三、四、五實際上都是去除子元素margin穿透父容器的方法,可以歸類為bfc法,本質相同。 在此表示感謝) 

      方法六:給父元素新增overflow:auto;和position:relative;同樣達到效果。

      此方法親測有效,是博友 @小精靈Pawn提供,在此表示感謝。

 

 

第四部分:margin值的單位為%時的幾種情況

    之前我舉例子時使用margin,它的值都是以px為單位的,這個理解起來沒有問題。但是如果margin值是以%為單位呢?實際上這時候百分比(%)是相對於該元素的父元素(容器),對於同級元素和父子元素都是如此。(再次感謝 博友@小精靈Pawn 提供的建議!!基於此建議補充這部分內容) 但是在同級元素中使用豎直方向的margin時會出現意想不到的結果,下面舉例說明。

  (1)同級元素在水平方向使用值為%的margin

  例7:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<head>
    <meta charset="UTF-8">
    <title>margin</title>
    <style>
        *{
            margin:0;
            padding:0;
        }
        .first{
            float: left;
            width: 200px;
            height: 200px;
            background: green;
        }
        .second{
            float: left;
            width: 200px;
            height: 200px;
            background: red;
            margin-left: 20%;
        }
    </style>
</head>
<body>
    <div class="first">寬為200,無margin</div>
    <div class="second">寬為200,margin-left為20%;</div>
</body>
</html>

 

這個例子中,設定兩個元素向左浮動,以便於觀察兩者水平方向的margin。其中左邊div無margin,右邊div的margin-left為20%,效果如下:

從效果圖可以看出兩個div之間的間距始終為父元素(這裡右邊div的父元素即為body,其寬度為瀏覽器寬度)的20%。

 

 

  

 

  (2)同級元素在豎直方向使用值為%的margin

   根據例7的啟發,我們可以猜想,如果在豎直方向上使用margin,且值的單位為%,那麼最終兩者之間的距離將是父元素(上例中為body)的百分數。那麼究竟是不是這樣呢?看下面的例子。

例8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<head>
    <meta charset="UTF-8">
    <title>margin</title>
    <style>
        *{
            margin:0;
            padding:0;
        }
        .first{
            width: 200px;
            height: 200px;
            background: green;
        }
        .second{
            width: 200px;
            height: 200px;
            background: red;
            margin-top: 10%;
        }
    </style>
</head>
<body>
    <div class="first">高為200,無margin</div>
    <div class="second">高為200,margin-top為20%;</div>
</body>
</html>

這裡設定上面的div無margin,下面的div的margin-top為10。效果如下:

 

我們發現,當我在縮小瀏覽器的高度時,豎直方向上的間距並沒有縮小!!! 而當我縮小瀏覽器的寬度時,豎直方向上的距離縮小了!!!

這就說明:統計元素之間在豎直方向上使用margin,當值的單位為%時,它是相對於父元素的寬度。

  那麼這裡為什麼不是如我們所希望的那樣相對於瀏覽器的高度呢?知乎上有大神是這樣解釋的(原文地址:https://www.zhihu.com/question/20983035?rf=27109182):

 

 

 

  (3)父子元素使用值為%的margin

    對於父子元素,如果在子元素中使用單位為%margin,那麼這個margin值是相對於父元素的寬度和高度(注意:這時的確是相對於父元素的高度!)的。

        例9  

    程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        *{
            margin:0;
            padding:0;
        }
        .father{
            width: 500px;
            height: 300px;
            background: red;
            overflow: hidden;
        }
        .son{
            width: 100px;
            height: 100px;
            background: green;
            margin-top: 20%;
            margin-left: 20%;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son"></div>
    </div>
</body>
</html>

  在這個例子中,我設定了margin-left的值為20%,margin-top的值為20%,父元素的width為500px,父元素的height為300px。下面看看效果吧。

從上圖可以看出子元素的margin-top值最終同樣是相對與父元素的寬度而非高度。

  

 

 

 

總結:

   這篇博文只介紹了margin的其中一部分知識點,但如果大家讀後能有些許收穫是再好不過的了。由於本人總結倉促,知識不足,錯誤在所難免,希望大家如果發現不妥之初能提出意見,我將非常感激。 

   補:將文章釋出之後,得到了很多建議和指導,給予了我很多幫助,也促使我不斷的修改博文,在此表示感謝!


 人之所以能,是相信能。 

相關文章