为什么给一个元素设置清除浮动后margin-top无效? (该元素和其前面的浮动元素有一个包裹层)

codepen在线演示:
http://codepen.io/medifle/pen/qdwpje

div.flo是左浮动元素, div.b3清除了浮动, 其margin-top却被忽略掉了. 注意这两个元素由一个section元素包裹.

下面是我观察到的一些情况:

  1. 如果没有这个section包裹, 则div.b3的margin-top有效.

  2. 如果给这个包裹用的section元素用任何防止margin collapsing的方法(比如说设置'overflow: hidden;', 加padding或border, 浮动, 绝对定位等), 则div.b3的margin-top有效.

  3. 如果没有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;
}
阅读 13.8k
10 个回答

一年后回来看这个问题,从@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。

  1. 第一种情况的clearance说的是把div.b3border边缘放在与div.flo底部外边缘平齐的位置所需要的必要数量。按照上述计算规则,当clearance出现的时候sectionmargindiv.b3margin不会collapse(具体见第二部分)

    
    H = M2 + C1
    C1 = H - M2
       = 10px - 1000px
       = -900px
  2. 第二种情况的clearance说的是把div.b3上边缘放在hypothetical position(也就是clearnone的时候)所需的必要数量。

    0 = M2 + C2
    C2 = -M2
       = -1000px

取两者最大值为-900px,即为clearance的大小。所以div.b3border上边缘始终会在div.flo底部外边缘的下方,但由于上述的计算规则,尤其注意第二种情况下div.b3不会因为顶部margin增加而改变位置。

关于margin collapsing

这一部分分析为什么div.b3的顶部margin不与section的顶部margin发生collapse

现在clearance处在section的顶部margindiv.b3的顶部margin之间,根据collapsing-margins的规定,sectiondiv.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,所以sectionfirst in-flow childdiv.b3

仔细研究了 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 便生效了。

不足之处欢迎补充讨论。

我想或许用F12看一下section的浏览器默认样式或许有发现

我之前貌似也遇到过同样问题,但因为什么忘记了,反正就是因为 clear 引起的。给父级加上 overflow: hidden; 就解决了。

清除浮动的clear:both不要用在b3上,用在b3上的话,再b3后面的元素才是清除浮动后的有效显示

你可以在flo和b3间添加一个<div style="clear:both"></div>来清除浮动

不用把清浮动和其他样式混着一起用

新手上路,请多包涵

我只想说,这和margin collapse有什么关系?
之所以不生效是因为margin只会对普通文档流的元素产生计算,其相邻的float元素已经脱离了文档流,自然没有作用。

  1. 如果没有包裹的 section,b3 上外边距同第一个 section 的下外边距属于相邻关系,故折叠

  2. 建立新BFC的元素的外边距不同其在文档流内子元素外边距折叠,因为二者不在一个块格式化上下文,父盒在父盒自己所在的,子盒在父盒创建的。故题主第二情况折叠

  3. 没有 flo 时候,b3 不需空隙生成,包裹 section 和 b3 属相邻关系,故折叠

  4. 凡生成了空隙,则外边距必不折叠。然而空隙可为负值,故再高的外边距遇到空隙,尽管不折叠,仍然像是被吞了一样。故@cool_zjy所言欠妥

详细可见鄙人所译CSS规范。

http://segmentfault.com/a/1190000003099116#articleHeader3
http://segmentfault.com/a/1190000003096320#articleHeader5

margin-top会作用到父容器上,
margin用来处理同级元素
了解下 fbc模式

新手上路,请多包涵
display: inline-block;
新手上路,请多包涵

其实是有效的,不行你把margin-top的值设成比浮动元素高度大时就看到效果了
图片描述

图片描述

推荐问题
宣传栏