codepen在线演示:
http://codepen.io/medifle/pen/qdwpje
div.flo是左浮动元素, div.b3清除了浮动, 其margin-top却被忽略掉了. 注意这两个元素由一个section元素包裹.
下面是我观察到的一些情况:
如果没有这个section包裹, 则div.b3的margin-top有效.
如果给这个包裹用的section元素用任何防止margin collapsing的方法(比如说设置'overflow: hidden;', 加padding或border, 浮动, 绝对定位等), 则div.b3的margin-top有效.
如果没有div.flo, 则div.b3的margin-top有效, 尽管会发生margin collapsing.
以上三个情况在熟悉了BFC和margin collapsing之后都好理解.
而这个在线演示的奇怪之处就在于它引入了clearance和margin collapsing, 我不明白的地方就是这两个效果共同作用的机理 . 我看了W3C上clearance和margin collapsing的内容, 然而并没有搞清原因.
stackoverflow上有几个一样的问题(其中有一个问题长达五年之久, 始于10年, 五年后的现在, 答案排在第一的作者虽然给出了解决办法, 但也坦言仍然没搞清楚原理), 可惜回答都没有满意的.
代码如下:
HTML:
<section style="height: 20px;">
</section>
<section>
<div class="flo"></div>
<div class="b3"></div>
</section>
<p>The bottom margin of div.b3 collapses with this p element. But the top margin of b3 with clearance is mysteriously ignored.</p>
CSS:
body {
font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
color: #fff;
}
section {
background: purple;
margin-top: 20px;
}
.flo {
width: 150px;
background: pink;
border: 5px dashed #ff9e2c;
float: left;
}
.b3 {
width: 100px;
height: 100px;
border: 8px solid lightgreen;
background: #4e97cd;
margin: 1000px 0 20px 0;
clear: both;
}
p {
background: #6AC5AC;
margin: 15px 0;
}
margin-top会作用到父容器上,
margin用来处理同级元素
了解下 fbc模式
我之前貌似也遇到过同样问题,但因为什么忘记了,反正就是因为 clear
引起的。给父级加上 overflow: hidden;
就解决了。
一年后回来看这个问题,从@HaoyCn的回答里得到了灵感,让我重新读clearance,发现答案其实就在那。
关于clearance(间隙)
这一部分分析为什么div.b3
顶部的margin
无论如何增大,div.b3
都保持在div.flo
的下方位置而不改变。
Values other than 'none' potentially introduce clearance.
Computing the clearance of an element on which 'clear' is set is done by first determining the hypothetical position of the element's top border edge. This position is where the actual top border edge would have been if the element's 'clear' property had been 'none'.
If this hypothetical position of the element's top border edge is not past the relevant floats, then clearance is introduced
Then the amount of clearance is set to the greater of:
The amount necessary to place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared.
The amount necessary to place the top border edge of the block at its hypothetical position.
The clearance can be negative or zero.
上述规范告诉我们, 当clear
属性不是none
的时候,hypothetical position
顶部border
的边缘没有低于对应的浮动元素底部外边缘(bottom margin)的时候,clearance
就会出现。
那么clearance
有多少呢?上述规范说了取两种情况下计算结果的最大值。
就本例而言:
假设:section
的顶部border
边缘为纵坐标0。div.b3
的顶部margin
是M2, div.flo
高度为H(含外边距)。第一种情况的clearance
为C1,第二种情况的clearance
为C2。
-
第一种情况的
clearance
说的是把div.b3
的border
边缘放在与div.flo
底部外边缘平齐的位置所需要的必要数量。按照上述计算规则,当clearance
出现的时候section
的margin
和div.b3
的margin
不会collapse
(具体见第二部分)H = M2 + C1 C1 = H - M2 = 10px - 1000px = -900px
-
第二种情况的
clearance
说的是把div.b3
上边缘放在hypothetical position
(也就是clear
为none
的时候)所需的必要数量。0 = M2 + C2 C2 = -M2 = -1000px
取两者最大值为-900px
,即为clearance
的大小。所以div.b3
的border
上边缘始终会在div.flo
底部外边缘的下方,但由于上述的计算规则,尤其注意第二种情况下div.b3
不会因为顶部margin
增加而改变位置。
关于margin collapsing
这一部分分析为什么div.b3
的顶部margin
不与section
的顶部margin
发生collapse
。
现在clearance
处在section
的顶部margin
和div.b3
的顶部margin
之间,根据collapsing-margins的规定,section
和div.b3
这两margin
不会collapse,相关规范的细节如下:
Adjoining vertical margins collapse, (except: ...)
Two margins are adjoining if and only if:
no line boxes, no clearance, no padding and no border separate them
both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
top margin of a box and top margin of its first in-flow child
本例中的情况适用于上述两种相邻关系(adjoining)。vertically-adjacent
虽然满足,但no clearance
这一条不满足,所以不会发生margin collapse。
当然规范在Note
里同样以更通俗的语言提到了这一点:
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
注意: 这里div.flo
不属于in-flow
,所以section
的first in-flow child
是div.b3
。
清除浮动的clear:both不要用在b3上,用在b3上的话,再b3后面的元素才是清除浮动后的有效显示
你可以在flo和b3间添加一个<div style="clear:both"></div>来清除浮动
不用把清浮动和其他样式混着一起用
如果没有包裹的 section,b3 上外边距同第一个 section 的下外边距属于相邻关系,故折叠
建立新BFC的元素的外边距不同其在文档流内子元素外边距折叠,因为二者不在一个块格式化上下文,父盒在父盒自己所在的,子盒在父盒创建的。故题主第二情况折叠
没有 flo 时候,b3 不需空隙生成,包裹 section 和 b3 属相邻关系,故折叠
凡生成了空隙,则外边距必不折叠。然而空隙可为负值,故再高的外边距遇到空隙,尽管不折叠,仍然像是被吞了一样。故@cool_zjy所言欠妥
详细可见鄙人所译CSS规范。
http://.com/a/1190000003099116#articleHeader3
http://.com/a/1190000003096320#articleHeader5
我想或许用F12看一下section的浏览器默认样式或许有发现
我只想说,这和margin collapse有什么关系?
之所以不生效是因为margin只会对普通文档流的元素产生计算,其相邻的float元素已经脱离了文档流,自然没有作用。
仔细研究了 W3C 文档中关于 clear 的部分,简单谈谈自己的理解。
先说自己的结论:
设置 clear 非 none 的元素的 margin-top 是否生效,主要取决于这个 margin-top 能否推动该元素越过所有 float 元素的。
其实很简单,可以参考下面代码:
<div style="float:left; background-color: #eee; height:20px; width:100px;"></div>
<div style="height: 10px;"></div>
<div style="clear:both; background-color: #aaa; margin-top:10px;">clear</div>
这里我们改变第三个 div 的 margin-top,可以看到:当 margin-top<10px 时,margin-top 不会表现,而被 clear 创建的间隙替代;当 margin-top>=10px 时,margin-top 才表现出来。
放到题主的例子中,虽然设置了 1000px 的 margin-top,但 margin-top 被折叠成为父元素的 margin-top,div.b3 根本没有 margin-top,不能将 div.b3 推动越过 div.flo 元素底部,因此该 margin-top 就被废掉了,而由浏览器生成间隙来将 div.b3 推动越过 div.flo(关于间隙如何生成请参见 W3C 的文档,有非常详细的描述)。
同时,也非常容易理解为什么 section 设置 overflow:hidden 触发 BFC 后 margin-top 随之生效。这时 margin collapse 被禁用,div.b3 实实在在拥有了 margin-top,同时这个 margin-top 足以推动其越过 div.flo,因此 margin-top 便生效了。
不足之处欢迎补充讨论。