原载:Smallni | http://www.smallni.com/collapsing-margin/
恩,margin叠加一直是个问题,也是我们一直会遇到的问题,很久以前就想把这个知识点整理下,做一个详细的讲解和分析。前一段时间知乎上有人问了这个问题,刚好克军也做了个回答,但回答的不尽其然,并且我做了自己的分析。刚好今天写到自己的博客里,并做一个更详细的探讨。
今天主要介绍的就是:到底神马是外边距叠加?神马情况下外边距会叠加?如何避免外边距叠加?
外边距叠加的定义
请先阅读w3c关于collapsing-margin的官方介绍:
在CSS中,两个或多个毗邻(父子元素或兄弟元素)的普通流中的块元素垂直方向上的 margin 会发生叠加。这种方式形成的外边距即可称为外边距叠加(collapsed margin)。
1、两个或多个
说明其数量必须是大于一个,又说明,叠加是元素与元素间相互的行为,不存在 A 和 B 折叠,B 没有和 A 叠加的现象。
2、毗邻
是指没有被非空内容、padding、border 或 clear 分隔开,说明其位置关系。
注意一点,在没有被分隔开的情况下,一个元素(非浮动元素等)的 margin-top 会和它普通流中的第一个子元素的 margin-top 相邻; 只有在一个元素的 height 是 “auto” 的情况下,它的 margin-bottom 才会和它普通流中的最后一个子元素(非浮动元素等)的 margin-bottom 相邻。
3、垂直方向
是指具体的方位,只有垂直方向的 margin 才会叠加,也就是说,水平方向的 margin 不会发生叠加的现象。
4、普通流
何为普通流?浮动(float)不是普通流,绝对定位(absolute)也不是普通流,恩,所以我们平常最普通的按序写的结构代码即为普通流。
何时会发生margin叠加?
margin叠加会发生在2种关系下,一种是父子元素,一种是兄弟元素。
来看demo:
1
2
3
<
div
style
=
"width: 500px; background: #000; height: auto; margin: 40px 0;"
>
<
div
style
=
"margin: 50px 0; width: 100px; height: 100px; background: #F90;"
>asdasd</
div
>
</
div
>
很明显,我们发现父元素和子元素发生了margin叠加,但要注意是第一个子元素。
再来看下面的DEMO:
1
2
<
div
style
=
"width:100px; height:100px; background:#000; margin:40px 0; "
></
div
>
<
div
style
=
"margin:50px 0; width:100px; height:100px; background:#F90;"
>asdasd</
div
>
恩,相邻的兄弟元素,上面的元素margin-bottom为40px,下面元素的margin-top为50px,最后你会发现2个元素垂直方向上的margin为50px,的确是发生叠加了,如果不发生折叠应该是50+40=90px。可是折叠了为什么是50px而不是40px?这涉及到了外边距叠加时的具体计算方法,下面会为大家讲解。
如何避免外边距叠加?
避免外边距叠加,其实很简单:外边距发生叠加的4个必要条件(2个或多个、毗邻、垂直方向、普通流),破坏任一个即可。
我们主要来谈一下“毗邻”,只有理解“毗邻”的概念之后,让元素互相之间不毗邻才能元素避免外边距的叠加。
毗邻不仅仅是兄弟元素之间,也有可能在父子元素之间,元素之间的外边距叠加只有在四种情况下才能叫毗邻:
- 一个元素的margin-top和它的第一个子元素的margin-top
top margin of a box and top margin of its first in-flow child - 普通流中一个元素的margtin-bottom和它的紧邻的兄弟元素的的margin-top
bottom margin of box and top margin of its next in-flow following sibling - 一个元素(height为auto)的margin-bottom和它的最后一个子元素的margin-bottom
bottom margin of a last in-flow child and bottom margin of its parent if the parent has ‘auto’ computed height - 一个没有创建BFC、没有子元素、height为0的元素自身的margin-top和margin-bottom
top and bottom margins of a box that does not establish a new block formatting context and that has zero computed ‘min-height’, zero or ‘auto’ computed‘height’, and no in-flow children
以上4种情况之间的元素都可能会发生外边距的折叠(最后一个是元素自身),但还要注意的是以上4种关系形成毗邻还要加一个条件:元素之间没有被非空内容、padding、border 或 clear 分隔开。
其中第一条父子元素要没有被非空内容、padding、border隔开,第二条兄弟元素之间要没有被非空内容隔开,第三条父子元素没之间要没有被非空内容、padding、border隔开。听起来可能有点乱,先上DEMO后上图,希望大家好理解一点:
1
2
3
4
5
<
div
style
=
"margin:100px 0; background:#990;"
>
<
div
style
=
"height:100px; border:1px solid #009; margin:100px 0;"
>A</
div
>
<
div
style
=
"height:100px 0; border:1px solid #009; margin:100px 0;"
>B</
div
>
<
div
style
=
"height:100px; border:1px solid #009; margin:100px 0"
>C</
div
>
</
div
>
W3C官方对于毗邻元素的margin-collapsing总结出了如下规则:
- Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
- Margins of elements that establish new block formatting contexts (such as floats and elements with ‘overflow’ other than ‘visible’) do not collapse with their in-flow children.
- Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
- Margins of inline-block boxes do not collapse (not even with their in-flow children).
- The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling, unless that sibling has clearance.
- The top margin of an in-flow block element collapses with its first in-flow block-level child’s top margin if the element has no top border, no top padding, and the child has no clearance.
- The bottom margin of an in-flow block box with a ‘height’ of ‘auto’ and a ‘min-height’ of zero collapses with its last in-flow block-level child’s bottom margin if the box has no bottom padding and no bottom border and the child’s bottom margin does not collapse with a top margin that has clearance.
- A box’s own margins collapse if the ‘min-height’ property is zero, and it has neither top or bottom borders nor top or bottom padding, and it has a ‘height’ of either 0 or ‘auto’, and it does not contain a line box, and all of its in-flow children’s margins (if any) collapse.
英文好的直接读,英文不好的就勉强看我的翻译吧:
- 浮动元素和其他任何元素之间不发生外边距叠加 (包括和它的子元素).
- 创建了BFC的元素不会和它的子元素发生外边距叠加
- 绝对定位元素和其他任何元素之间不发生外边距叠加(包括和它的子元素).
- inline-block元素和其他任何元素之间不发生外边距叠加 (包括和它的子元素).
- 普通流中的块级元素的margin-bottom永远和它相邻的下一个块级元素的margin-top叠加(除非相邻的兄弟元素clear)
- 普通流中的块级元素(没有border-top、没有padding-top)的margin-top和它的第一个普通流中的子元素(没有clear)发生margin-top叠加
- 普通流中的块级元素(height为auto、min-height为0、没有border-bottom、没有padding-bottom)和它的最后一个普通流中的子元素(没有自身发生margin叠加或clear)发生margin-bottom叠加
- 如果一个元素的min-height为0、没有border、没有padding、高度为0或者auto、不包含子元素,那么它自身的外边距会发生叠加
亲,你晕了吗,反正我看完这些全部是晕的差不多了,但不要烦,一定要定下心来,总结出一些规律出来,如下:
1.父子元素(分2种情况)之间发生margin叠加的条件:
- 父元素和第一个子元素发生margin-top叠加
- 父元素没有创建BFC
- 父元素和第一个子元素之间没有非空内容
- 父元素没有border-top
- 父元素没有padding-top
- 父元素和最后一个子元素发生margin-bottom叠加
- 父元素没有创建BFC
- 父元素height为auto、min-height为0
- 父元素和最后一个子元素之间没有非空内容
- 父元素没有border-bottom
- 父元素没有padding-bottom
2种情况都总结了,那么让他们不发生外边距叠加也就显得很容易了:
- 为父元素创建BFC
- 为父元素设置相应的padding或者border
2.兄弟元素之间发生外边距叠加的条件:
- 兄弟元素都不是float元素
- 兄弟元素都不是absolute元素
- 兄弟元素都不是inline-block元素
那么让它们之间不发生外边距叠加也显得很容易了啊:
- 让兄弟元素float
- 让兄弟元素absolute
- 让兄弟元素inline-block
恩,对于这个解决外边距折叠的方法你满意么?!可是有时我们根本不需要给元素浮动或者绝对定位或者行内块级啊!所以这根本是一个很坑爹的解决方案。那么如何解决?
其实外边距叠加margin-collapsing一直以来就是W3C所制定的一个规范,为了防止元素之间不小心同时设置了margin而不是期望中的效果,这本是一个合理存在的东西却被我们当做BUG来处理,这也是一个历史遗留了很久的问题,不要把它当做BUG来看,当做一个规范来看吧。其实最好的解决方案是在写结构的时候尽量去写一个方向的margin(top or bottom),这样不就不会发生这样的问题了吗?
前端是一个知识面很广很深的行业,不一定你每样东西了解一点就很牛逼的,还需要有专研精神,因为有时一个很简单的问题可能就反应出你对基础知识和原理的理解。理解margin-collapsing这个概念对于我们日后的学习还是很有帮助的,在没写这篇文章之前,如果没有去详细看W3C的原文,我也不知道一个元素自身会发生外边距叠加。
各种情况,熟记于心,才能在下次写代码时得心应手。
PS:外边距叠加时的计算方法本人不做更多的解释,这里有很详细很好的解释