外边距折叠是CSS布局中很小的一个知识点,但是却能够对我们的页面布局产生很大的影响。这篇文章详细介绍了外边距折叠的情况及如何避免,希望能够帮助大家加深理解。水平有限,翻译不当还请指正。
文章内容略显啰嗦,可以重点关注代码部分
让我们来探究一下外边距折叠的结果是什么,它将怎样影响页面上的元素。
W3C规范是这样定义外边距折叠的:
In this specification, the expression collapsing margins means that adjoining margins (no non-empty content, padding, or border areas, or clearance separate them) of two or more boxes (which may be next to one another or nested) combine to form a single margin.
简单说,这个定义表明当垂直方向两个元素的外边距(译:即margin
)相接触时,只有margin
值较大的会被选用,而margin
较小的元素的外边距会被折叠为0。在其中一个元素的margin
为负值的情况下,两元素的外边距是两者求和。如果两个元素的外边距都是负值,那么取较小的负值作为最终外边距(如-3px,-5px,取-5px)。这个定义适用于相邻或嵌套的元素上。
在一些情况下元素的外边距不会折叠:
- 浮动元素
- 绝对定位元素
- 行内块元素
- 设置了
overflow
不为visible
的元素(不会与其子元素发生外边距折叠) cleared
元素(上外边距不会与其父块的下外边距折叠。译:此处不太理解,原文是They do not collapse their top margins with their parent block’s bottom margin.)- 根元素
这是一个很难理解的概念,让我们来看一些例子。
相邻元素间的外边界折叠
相邻元素的外边距折叠。简言之,就是文档流(译:normal document flow,国内常译为文档流)中垂直方向相邻的块级元素,只有margin
值最大的元素的外边距会被采用,而margin
较小的元素的外边距会被折叠为0。例如,如果一个元素的margin-bottom
为25px
,在其下方相邻的元素的margin-top
为20px
,那么只有25px
的下外边距会被应用,即两元素间的距离为25px
,而不是45px
(25+20)。
这个行为最好通过一个简短的例子说明,请看下面的代码:
h1 {
margin: 0 0 25px 0;
background: #cfc;
}
p {
margin: 20px 0 0 0;
background: #cf9;
}
如图1所示,两个元素间的间隙只有25px
,较小的外边距被折叠为0。如果上述例子中元素的外边距相等(如,都是20像素),那么两元素间的距离就只是20px
。
有一种情况下会发生轻微的偏差:如果其中一个元素的上或下外边距为负数,那么最终的元素间距为正数与负数的和。下面这个示例展示了这一点:
h1 {
margin: 0 0 25px 0;
background: #cfc;
}
p {
margin: -20px 0 0 0;
background: #cf9;
}
h1
元素的下外边距是一个正数(25px
),p
元素的上外边距是一个负数(-20px
),在这种情况下,这两个值将进行求和计算得到最终的间距:25px
+ (-20px
) = 5px
。
如果计算的结果是一个负数,将会导致一个元素覆盖另外一个元素。你可能会说负的外边距使元素在相反的方向上生成正的外边距,关于负的外边距的更多信息请查阅外边距。(译:这句话翻译起来实在别扭,自行理解吧,也欢迎你在评论里给出自己的见解)
父子元素间的外边距折叠
目前为止,我们只讨论了相邻元素间的外边距折叠,但同样的情况适用于外边界接触的父子元素间。这里的接触是指在相邻的margin
之间没有padding
,border
和content
。在下面的例子中,一个父元素包含了一个被设置了上外边距的子元素:
h1 {
margin: 0;
background: #cff;
}
div {
margin: 40px 0 25px 0;
background: #cfc;
}
p {
margin: 20px 0 0 0;
background: #cf9;
}
在上边的样式表中,为p
元素声明了一个上外边距,在下边的代码中可以看到p
元素是div
元素的子元素。
<h1>Heading Content</h1>
<div>
<p>Paragraph content</p>
</div>
图2中可以看到这段代码的结果:
你可能会认为p
元素和h1
元素的间距为60px
,因为div
元素的margin-top
是40px
,并且p
元素的margin-top
为20px
。你或许也会认为在p
元素的上方会有20px
的范围展示div
元素的背景色。但是如图所示,并非如此,因为发生了外边距折叠,导致只有最大的外边距被应用(和相邻块一样)。
事实上如果div
元素没有上外边距(译:即margin-top
为0)并且p
元素的margin-top
为40px
我们会得到相同的结果。p
元素的40px
上外边距转化成div
元素的上外边距,将div
元素下移40px
,并使p
元素紧贴div
顶部。在div
元素内p
元素的上方不会有背景色展示。
为了使两个元素的外边距都展示,并且使div
的背景色展示在p
元素的上方,这里需要设置一个border
或者padding
去阻止外边距折叠。我们简单的为div
元素添加一个上边框,就可以得到最初期待的效果:
h1 {
margin: 0;
background: #cff;
}
div {
margin: 40px 0 25px 0;
background: #cfc;
border-top: 1px solid #000;
}
p {
margin: 20px 0 0 0;
background: #cf9;
}
在图3中,我们可以看到div
元素与h1
元素的距离依旧是40px
,但是p
元素被下移了20px
的距离,因此显示了20px
范围的div
背景色(通过设置外边框)。
如果我们不希望在设计上展示可见的上边框,1px
的上内边距会产生同样的效果。记住border
和padding
应该应用在父div
上,在p
元素上不会阻止外边距折叠,因为p
元素的margin
在border
的外面。
上述示例只讨论了单个父元素和单个子元素外边距接触的情况,同样的处理方式也适用于多重子孙元素(即,嵌套的元素。译:原文是several children,字面意思是多个子元素,但实际指多重嵌套的子孙元素)均含有垂直相邻外边距的情况:即所有的外边距会折叠为一个单独的外边距。虽然上面的示例只提到了上外边距,但同样的效果作用于下外边距,下文会看到。
在下面的示例中,我们嵌套了四层div
元素,每一个都设置了10px
的外边距。每个div
都设置了不同的背景色,所以我们可以清楚的看到外边距折叠的效果:
.box {
margin: 10px;
}
.a {
background: #777;
}
.b {
background: #999;
}
.c {
background: #bbb;
}
.d {
background: #ddd;
}
.e {
background: #fff;
}
图4展示了上述 CSS 的结果。
正如这个例子所示,这段CSS的效果是引人注目的:所有的垂直方向外边距折叠为一个单独的,10px
的外边距。不同于水平方向所示的每个外边距都可见,虽然我们为每个元素设置了不同的背景色,但垂直方向并没有展示类似的颜色。整个块将会被放置在距文档流中其他元素10px
的位置上,所有嵌套的块的外边距将会折叠成一个。
如上所述,阻止外边界折叠最简单的方法是为每个元素添加内边距或边框。如果我们希望每个元素间的外边距是10px
,可以通过简单的设置一个9px
的margin
和1px
的padding
来实现:
.box {
margin: 9px;
padding: 1px;
}
微小的改动会阻止垂直方向上的外边距折叠,效果图5所示。
当然,考虑IE的兼容性是很重要的。第一个例子(图4)中的元素在IE中会直接展示位图5的效果。另外值得一提的是在IE之外的其他浏览器中,设置overflow
为不等于visible
的其他值时会产生同样的效果。
总结
尽管外边距折叠给人的第一印象不够直观,但它确实使得多层元素嵌套的情况更加简单,也更令人满意。如果有需要你也可以使用上述简单方式阻止外边距折叠。
那么,现在你真的读懂外边距折叠了吗?不如我们来做个小小的测试吧。对A,B,C三个块级元素分别设置margin
如下图所示,请问:
- 把B和C同时放到A中,BC间距是多少?
- 基于问题1,把B的
margin
改为-30px
呢? - 基于问题1,把BC的
margin
均取负数呢? - 把B放入A内,C放入B内时,ABC的位置是怎样的?
- 基于问题4,B的
margin
为-30px
呢?
实践出真知,不确定答案的同学还是亲自动手试试吧:)