本文启发于博客园一篇2016年的“深刻理解css中的margin属性”文章,根据当前浏览器(
chrome 71.0.3578.98(正式版本) (64 位)),加上本身的理解,每一个例子都是亲测过css
这篇博文主要分为如下几个部分:html
margin--基础知识
margin--在同级元素(非父子关系)之间应用
margin--在父元素和子元素之间应用(重点)
margin--margin值的单位为%时的几种状况chrome
要介绍margin的基础知识,咱们不可回避地要谈到css盒子模型(Box Model),通常而言,css盒子模型是用来设计和布局的。它本质上是一个盒子,包括:外边距(margin)、边框(border)、内边距(padding)以及最中间的内容(content)。下图即为盒子模型(这里只谈W3C规范的标准盒模型,而不谈IE5和IE6在怪异模式中使用的非标准的盒子模型):浏览器
咱们要介绍的margin在最外层,由于margin(外边距)必定是透明的,因此它能够用来使得不一样的盒子之间留有必定的间隙从而达到布局美观等效果。从上面的盒子模型中咱们能够看到,margin在四周均存在,咱们可使用margin-top、margin-right、margin-bottom、margin-left分别设置这四个方向的margin值。(注:因为这部分知识较为基础,因此我再也不在这部分不作更多介绍)布局
这一部分主要介绍水平方向和竖直方向的外边距的合并问题。spa
####(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>
效果以下:code
这时二者之间的距离恰好为100px。htm
补充说明:你们能够看到,为了使得两个div(块状元素)脱离正常的文档流我使用了display:inline-block;属性,另外,我还把body的font-size设置为0,这样能够解决inline-block自身的问题(若是不清楚这里的描述能够看个人博文《理解与应用css中的display属性》,这篇文章介绍了inline-block存在的问题),不然两个div的举例会大于100px。固然使用float也可使得两个div出如今同一行中。blog
####(2)竖直方向的外边距合并
两个竖直方向的盒子相遇时,其竖直方向的距离等于上方盒子的下外边距和下方盒子的上外边距中较大的一个。
例2:代码以下:
`
<head>
<meta charset="UTF-8"> <title>水平方向的两个盒子</title> <style> .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值的叠加。代码以下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"> <title>水平方向的两个盒子</title> <style> .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:
<!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值。
<!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,因此没法观察出来。
按照前面的经验,理论上来讲,咱们一样能够经过设置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也不会有任何影响,因此这也是一种不错的方法。
上述方法3、4、五实际上都是去除子元素margin穿透父容器的方法,能够归类为bfc法,本质相同。
以前我举例子时使用margin,它的值都是以px为单位的,这个理解起来没有问题。可是若是margin值是以%为单位呢?实际上这时候百分比(%)是相对于该元素的父元素(容器),对于同级元素和父子元素都是如此。(再次感谢 博友@小精灵Pawn 提供的建议!!基于此建议补充这部份内容) 可是在同级元素中使用竖直方向的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%。
根据例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...):
对于父子元素,若是在子元素中使用单位为%margin,那么这个margin值是相对于父元素的宽度和高度的。
例9 代码以下:
<!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值最终一样是