深入理解 margin collapse 及 BFC
外边距融合对于前端的同学应该是不陌生,之前当我遇到这种问题时,可能很多人随手换个 css 属性解决后即不在思考或总结这个问题的解决办法,前几天看到这么一个讨论,发现出现了很多之前没有见过的 css 方面的名次,感觉需要去深入的了解一下这块了。
这节内容主要说明 margin collapse 及 BFC 这两个东西,主要参考 MDN 及 css tricks,Pet 的博客 的这几篇文章。
margin collapse
块的顶部外边距和底部外边距有时被组合(折叠)为单个外边距,其大小是组合到其中的最大外边距,这种行为称为外边距塌陷(margin collapsing),有的地方翻译为外边距合并。
发生外边距塌陷的三种基本情况:
相邻的兄弟姐妹元素
毗邻的两个兄弟元素之间的外边距会塌陷(除非后者兄弟姐妹需要清除过去的浮动)。例如:
1 2 | <p style="margin-bottom: 30px;">这个段落的下外边距被合并...</p> <p style="margin-top: 20px;">...这个段落的上外边距被合并。</p> |
可以发现这两个段落中间的距离,不是 ”上面段落的下边距“ 与 ”下面段落的上边距“ 的 求和 ,而是两者中的较大者(在此示例中为30px)。
块级父元素与其第一个/最后一个子元素
如果块级父元素中,不存在上边框、上内边距、内联元素、块格式化上下文、 清除浮动 这五条(也可以说,当上边框宽度及上内边距距离为0时),那么这个块级元素和其第一个子元素的上边距就可以说”挨到了一起“。此时这个块级父元素和其第一个子元素就会发生上外边距合并现象,换句话说,此时这个父元素对外展现出来的外边距将直接变成这个父元素和其第一个子元素的margin-top的较大者。
类似的,若块级父元素的 margin-bottom 与它的最后一个子元素的margin-bottom 之间没有父元素的 border、padding、inline content、height、min-height、 max-height 分隔时,就会发生 下外边距合并 现象。
空块元素
如果存在一个空的块级元素,其 border、padding、inline content、height、min-height 都不存在。那么此时它的上下边距中间将没有任何阻隔,此时它的上下外边距将会合并。例如:
1 2 3 4 5 | <p style="margin-bottom: 0px;">这个段落的和下面段落的距离将为20px</p> <div style="margin-top: 20px; margin-bottom: 20px;"></div> <p style="margin-top: 0px;">这个段落的和上面段落的距离将为20px</p> |
当以上情形同时出现时,外边距合并会更加复杂(会比较两个以上外边距来最终计算出真实的边距值)。
即使外边距为0,这些规则也仍旧生效。因此,无论父元素的外边距是否为0,第一个或者最后一个子元素的外边距会被父元素的外边距”截断”(根据上面的规则),在负外边距的情况下,合并后的外边距为最大正外边距与最小负外边距之和。
当有负边距存在时,合并后的外边距将是最大正边距加上最小负边距(即负边距中绝对值最大的一个)。
如两个兄弟元素,上面的元素的下边距为 20px ,下面的元素的上边距为 -20px ,那么发生外边距合并后,这两个元素的实际距离将变成 0px 。
BFC(Block Formatting Context 块格式化上下文)与元素外边距合并 :
- 当两个元素属于不同的BFC时,这两个元素的外边距不会合并。
- 但在同一个BFC内,两个相邻元素的外边距仍会合并。
具体 margin collapse 问题怎么表现,可以看 css tricks 的文章,里边有具体的例子及代码。
怎么解决呢
其实解决办法很简单,css tricks 上是用的 padding。其实只要存在 上边框、上内边距、内联元素、块格式化上下文、 清除浮动 这些属性,就不会有 margin collapse 问题。
块格式化上下文
块格式化上下文(Block Formatting Context,BFC),是Web页面的可视化CSS渲染的部分,是块级盒布局发生的区域,也是浮动元素与其他元素交互的区域。
创建块格式化上下文的方式如下:
- 根元素或其它包含它的元素
- 浮动元素 (元素的 float 不是 none)
- 绝对定位元素 (元素的 position 为 absolute 或 fixed)
- 内联块元素 (元素具有 display: inline-block)
- 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
- 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
- 匿名表格元素 (元素具有 display: table, table-row, table-row-group, table-header-group, table-footer-group [分别是HTML tables, table rows, table bodies, table headers and table footers的默认属性],或 inline-table )
- overflow 值不为 visible 的块元素,
- display 值为 flow-root 的元素
- contain 值为 layout, content, 或 strict 的元素
- 弹性元素 (display: flex 或 inline-flex元素的子元素)
- 网格元素 (display: grid 或 inline-grid 元素的子元素)
- 多列容器 (元素的 column-count 或 column-width 不为 auto 即视为多列,column-count: 1的元素也属于多列)
- 即便具有 column-span: all 的元素没有被包裹在一个多列容器中,column-span: all 也始终会创建一个新的格式化上下文。
- 块格式化上下文包含创建它的元素内的所有内容。
块格式化上下文对于定位 (参见 float) 与清除浮动 (参见 clear) 很重要。定位和清除浮动的样式规则只适用于处于同一块格式化上下文内的元素。浮动不会影响其它块格式化上下文中元素的布局,并且清除浮动只能清除同一块格式化上下文中在它前面的元素的浮动。
注意:Margin collapsing也只发生在属于同一块格式化上下文的块级元素之间。