Skip to main content

外边距重叠

块的 上外边距 (margin-top)下外边距 (margin-bottom) 有时合并 (折叠) 为单个边距,其大小为单个边距的最大值 (或如果它们相等,则仅为其中一个),这种行为称为 边距折叠

css 2.0 规范对 margin 重叠有如下的描述:

  • 水平边距永远不会重合。
  • 垂直边距可能在特定的框之间重合:
  • 常规流向中两个或多个块框相邻的垂直边距会重合。结果的边距宽度是相邻边距宽度中较大的值。如果出现负边距,则在最大的正边距中减去绝对值最大的负边距。如果没有正边距,则从零中减去绝对值最大的负边距。
  • 在一个浮动框和其它框之间的垂直边距不重合。//这句话是不够严谨,在 IE 浏览器下确实如此,但是 Firefox 等浏览器下依旧重合。
  • "绝对定位的框"与"相对定位的框"边距不重合。//这句话有待斟酌,我在 Firefox 等浏览器下测试,效果貌似很糟糕的。
  • 当两个元素的垂直外边距相接触时,只有外边距值最大的元素的外边距会被尊重,而外边距值较小的元素的外边距会被折叠为零。在一个元素具有负边距的情况下,将边距值加在一起以确定最终值。如果两者都是负数,则使用较大的负值。此定义适用于相邻元素和嵌套元素。

在其他情况下,元素的边距不会折叠:

  • 浮动元素
  • 绝对定位元素
  • 行内块元素
  • 溢出设置为可见以外的任何元素(它们不会折叠其子元素的边距。)
  • 已清除的元素(它们不会将其上边距与其父块的下边距折叠起来。)
  • 根元素

外边距折叠

同一层相邻元素之间

相邻的两个元素之间的外边距重叠,除非后一个元素加上 clear-fix 清除浮动

Loading Github Gist ...

如果以为边界会合并的话,理所当然会猜测上下 2 个元素会合并一个 100px 的边界范围,但其实会发生边界折叠,只会挑选最大边界范围留下,所以这个例子的边界范围其实是 60px。

有一种情况会导致与折叠边距的行为略有不同:如果其中一个元素的顶部或底部边距为负,则正负边距将加在一起以达到最终的真实边距。

Loading Github Gist ...

h1 元素的下边距是正数 (40px),p 元素的上边距是负数 (-20px)。在这种情况下,将两个数字相加计算最终边界:40px + (-20px) = 20px。

如果此计算结果为负数,则此值将产生一个元素与另一个元素重叠的效果。

没有内容将父元素和后代元素分开

如果没有边框 border,内边距 padding,行内内容,也没有创建 块级格式上下文清除浮动 来分开一个块级元素的上边界 margin-top 与其内一个或多个后代块级元素的上边界 margin-top;或没有边框,内边距,行内内容,高度 height,最小高度 min-height 或最大高度 max-height 来分开一个块级元素的下边界 margin-bottom 与其内的一个或多个后代后代块元素的下边界 margin-bottom,则就会出现父块元素和其内后代块元素外边界重叠,重叠部分最终会溢出到父级块元素外面。

空的块级元素

当一个块元素上边界 margin-top 直接贴到元素下边界 margin-bottom 时也会发生边界折叠。这种情况会发生在一个块元素完全没有设定边框 border、内边距 padding、高度height、最小高度 min-height 、最大高度 max-height 、内容设定为 inline 或是加上 clear-fix 的时候。

Loading Github Gist ...

不会发生外边距折叠

  • 行内块元素 inline-block 不会发生外边距折叠,包括同层级和嵌套元素。
  • 浮动 float 元素不会发生外边距折叠,包括同层级和嵌套元素。
  • 绝对定位元素 absolute 不会发生外边距折叠,包括同层级和嵌套元素。
  • 创建了 BFC 的元素不会和它的子元素发生外边距折叠。
注意事项
  • 折叠现象发生在块级元素,且是 top 和 bottom 的外边距,因此与行内元素无关,再说行内元素上下外边距天然就不会生效,如果当做生效,那么行内元素只要有 top 和 bottom 天然就会折叠。
  • 外边距折叠,可能会产生子元素溢出父元素的现象,不论父元素外边距是否为 0。
  • 当相邻元素和嵌套元素都发生外边距折叠,那么情况将更复杂。
  • 折叠让他折叠,设置外边距在两个靠近的元素中,只设置一边的 top 或 bottom,这可能是个好习惯。

相邻元素和嵌套元素总结

margin 合并的计算规则总结为 "正正取大值" "正负值相加" "负负最负值"

尽管说我们应该努力的方向是尽量避免产生外边距折叠,而不是去结算折叠后的值,一旦产生折叠现象,我就改代码,让他不产生折叠,而不是产生之后去计算折叠的值。

  • 同正或同负:选绝对值最大的。

    • 比如 -50px 和 -70px 发生折叠,那么折叠后的值是 70px,因为 -70 的绝对值最大。
    • 比如 -50px 和 -50px,此时绝对值最大是 50px ,所以只有一个选择 50px。
    • 比如 50px 和 70px 发生折叠,那么折叠后的值是 70px,因为 70 的绝对值最大。
    • 比如 50px 和 50px,当然也选绝对值最大的 50px。
  • 有正有负:最大的正边距与最小的负边距的和

    • 例如 70px、-50px 发生折叠,那么边界范围就是 70px + (-50px)= 20px。

margin 合并的意义

每枚硬币都有正反面,其实 margin 重叠也是又它的用处的。当垂直方向上有多个模块时,margin 重叠正好可以让上中下都有一个 margin 值,而且由于 margin 重叠,所以 margin 值的表现都是一样的。

推荐使用 em 作为单位。为何使用 em 作为单位也很好理解,大家应该知道浏览器默认的字号大小是可以自定义的吧,例如,默认的是 16 像素,假如我们设置成更大号的字号,同时 HTML 标签的 margin 是像素大小,则会发生文字变大但是间距不变的情况,原本段落有致的阅读体验必然又会变得令人窒息。em 作为相对单位,则可以让我们的文章或新闻无论多大的字体都排版良好。可以看到,HTML 标签默认内置的 CSS 属性值完全就是为了更好地进行图文信息展示而设计的。

如果你为网页上的文字元素添加 padding 时会希望 padding 值能与字体大小成比例,绝对单位是无法实现的。但是如果用 em 单位来指定 padding,它就能根据字体大小而改变。

  • 对于兄弟元素的 margin 合并其作用和 em 类似,都是让图文信息的排版更加舒服自然。假如说没有 margin 合并这种说法,那么连续段落或列表之类首尾项间距会和其他兄弟标签成 1:2 关系;文章标题距离顶部会很近,而和下面的文章详情内容距离又会很开,就会造成内容上下间距不一致的情况。这些都是糟糕的排版体验。而合并机制可以保证元素上下间距一致,无论是 <h2> 标题这种 margin 偏大的元素,还是中规中矩的 <p> 元素,因为 "正正取大值"。

  • 父子 margin 合并的意义在于:在页面中任何地方嵌套或直接放入任何裸 <div>,都不会影响原来的块状布局。<div> 是网页布局中非常常用的一个元素,其语义是没有语义,也就是不代表任何特定类型的内容,是一个通用型的具有流体特性的容器,可以用来分组或分隔。由于其作用就是分组的,因此,从行为表现上来看,一个纯粹的 <div> 元素是不能够也不可以影响原先的布局的。

  • 自身 margin 合并的意义在于可以避免不小心遗落或者生成的空标签影响排版和布局。

参考