乐趣区

深入理解css中的margin属性(最新浏览器支持)

本文启发于博客园一篇 2016 年的“深入理解 css 中的 margin 属性”文章,根据当前浏览器(chrome 71.0.3578.98(正式版本)(64 位)),加上自己的理解,每个例子都是亲测过
这篇博文主要分为以下几个部分:
margin– 基础知识 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:代码如下:

<!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:代码如下:
`<!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>
效果如下:

![clipboard.png](/img/bVbnQFi)

** 这时我们肉眼都可以观察出来,两者竖直方向的举例大约为 100px(实际就是 100px)而非 100+50=150px;这正是因为两个竖直方向的盒子相遇时,其竖直方向的距离等于上方盒子的下外边距和下方盒子的上外边距中较大的一个。**

另外一个有趣的例子就是:假设有一个元素同时设置了 margin-top 和 margin-bottom,但是内容为空,那么这两个 margin 值也会叠加,值为两者最大的一个,它类似与竖直方向上两个盒子 margin 值的叠加。代码如下:
<!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>
最终的效果如下:

![clipboard.png](/img/bVbnQFs)

** 我们发现这时在上面的 div 和在下面的 div 之间的举例并不是 100+50=150px,而是两者中的最大者,即 100px。**


那么 W3C 为什么会设定这样的标准而不设定和水平方向一样的标准呢?即 margin 值的叠加,实际上这也是有一定的道理的。比如我们需要设计一个由若干个段落构成的一个页面。我们需要设置 margin-top 和 margin-bottom 使得第一段和页面的最上方有一段距离,使得最后一段和最下方有一段距离。下面是不叠加和叠加的效果图:

![clipboard.png](/img/bVbnQFM)

我们可以看到左边的页面没有重叠,那么两个段落之间的举例就是最上方的两倍间距了,而右边的页面发生了重叠,则所有的间距都是相等的。或许这就是这样设定标准的目的吧,谁知道呢?

### 第三部分:margin– 在父元素和子元素之间应用(重点)
第二部分介绍了同级元素之间使用 margin,而这一部分将要介绍最有意思的父元素和子元素之间 margin 的应用。这一部分,我们同样从两个方面来讨论。一方面是子元素设置水平方向上的 margin 值,另一方面是子元素设置竖直方向的 margin 值。

####(1)在子元素中设置水平方向的 margin 值
我们可以设置 margin-left 来控制子元素的左边框和父元素的左边框之间的举例。
例 3:
<!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; 效果如下:

![clipboard.png](/img/bVbnQFU)

即子元素的左边框和父元素的左边框之间的距离为 100px。与在同级元素之间设置 margin 不同,因为同级元素之间的 margin 不会考虑到 padding, 但是在父元素和子元素就不同了,那么如果父元素中如果有 padding,效果会是什么样的呢?请看下面一个例子:

例 4:下面我们在上面例子的基础上给父元素添加 padding 值。

<!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 值,如下所示:
<!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:
<!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, 或者 overflow:auto; 方法四:给父元素或者子元素声明浮动 float 方法五:使父元素或子元素声明为绝对定位:position:absolute;
方法一:基于例 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 也不会有任何影响,所以这也是一种不错的方法。
上述方法三、四、五实际上都是去除子元素 margin 穿透父容器的方法,可以归类为 bfc 法,本质相同。
第四部分:margin 值的单位为 % 时的几种情况
之前我举例子时使用 margin,它的值都是以 px 为单位的,这个理解起来没有问题。但是如果 margin 值是以 % 为单位呢?实际上这时候百分比(%)是相对于该元素的父元素(容器),对于同级元素和父子元素都是如此。(再次感谢 博友 @小精灵 Pawn 提供的建议!!基于此建议补充这部分内容)但是在同级元素中使用竖直方向的 margin 时会出现意想不到的结果,下面举例说明。
(1)同级元素在水平方向使用值为 % 的 margin
例 7:
<!DOCTYPE html>
<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
<!DOCTYPE html>
<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/questio…):

(3)父子元素使用值为 % 的 margin
对于父子元素,如果在子元素中使用单位为 %margin,那么这个 margin 值是相对于父元素的宽度和高度的。
例 9 代码如下:
<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 值最终同样是
![图片上传中 …]

退出移动版