[译]你真的了解外边距折叠吗

译自:Collapsing Margins

外边距折叠是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-bottom25px,在其下方相邻的元素的margin-top20px,那么只有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之间没有paddingbordercontent。在下面的例子中,一个父元素包含了一个被设置了上外边距的子元素:

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-top40px,并且p元素的margin-top20px。你或许也会认为在p元素的上方会有20px的范围展示div元素的背景色。但是如图所示,并非如此,因为发生了外边距折叠,导致只有最大的外边距被应用(和相邻块一样)。
事实上如果div元素没有上外边距(:即margin-top为0)并且p元素的margin-top40px我们会得到相同的结果。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背景色(通过设置外边框)。
设置border消除外边界折叠

如果我们不希望在设计上展示可见的上边框,1px的上内边距会产生同样的效果。记住borderpadding应该应用在父div上,在p元素上不会阻止外边距折叠,因为p元素的marginborder的外面。
上述示例只讨论了单个父元素和单个子元素外边距接触的情况,同样的处理方式也适用于多重子孙元素(即,嵌套的元素。:原文是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,可以通过简单的设置一个9pxmargin1pxpadding来实现:

.box {
  margin: 9px;
  padding: 1px;
}

微小的改动会阻止垂直方向上的外边距折叠,效果图5所示。
阻止外边距折叠

当然,考虑IE的兼容性是很重要的。第一个例子(图4)中的元素在IE中会直接展示位图5的效果。另外值得一提的是在IE之外的其他浏览器中,设置overflow为不等于visible的其他值时会产生同样的效果。

总结

尽管外边距折叠给人的第一印象不够直观,但它确实使得多层元素嵌套的情况更加简单,也更令人满意。如果有需要你也可以使用上述简单方式阻止外边距折叠。


那么,现在你真的读懂外边距折叠了吗?不如我们来做个小小的测试吧。对A,B,C三个块级元素分别设置margin如下图所示,请问:

  1. 把B和C同时放到A中,BC间距是多少?
  2. 基于问题1,把B的margin改为-30px呢?
  3. 基于问题1,把BC的margin均取负数呢?
  4. 把B放入A内,C放入B内时,ABC的位置是怎样的?
  5. 基于问题4,B的margin-30px呢?

实践出真知,不确定答案的同学还是亲自动手试试吧:)

posted @ 2017-11-01 23:09  闲鱼~  阅读(760)  评论(0编辑  收藏  举报