清除浮动后margin-top无效

77

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both; margin-top: 200px;">Main Data</div>

为什么上面的代码中'Main Data'的'margin-top'不起作用?

你使用的是哪个浏览器?我已经用过Firefox、IE和Chrome进行了检查。 - Zain Shaikh
5
这里的大部分答案提供了解决该行为的变通方法,这很好,但如果您想要一个(冗长、困难的)回答,实际上解释了为什么它首先会发生,请参见我的回答 - Mark Amery
大多数答案都是在回答“如何”而不是“为什么”! - Ayub
8个回答

75

你可以把这两个浮动的

放进另一个
里,然后设置其"overflow: hidden"属性:

<div style='overflow:hidden'>
  <div style="float: left;">Left</div>
  <div style="float: right;">Right</div>
</div>
<div style="clear: both; margin-top: 200px;">Main Data</div>

编辑 — 给这个5年前的答案添加一点内容:我认为混乱行为的原因是有点复杂的 外边距折叠 过程。对于来自OP的原始HTML,一个好的技巧是添加如下CSS规则:

div { border: 1px solid transparent; }

哇!现在(没有我额外添加的<div>)它运行得很好!嗯,除了那个来自边框的额外像素。特别是,我认为这是clear:both工作方式和边距折叠规则的组合,导致了原始代码中意外的布局。

再次编辑 - 对于完整(而且我认为是完全准确的)的故事,请参见Mark Amery的精彩回答。细节有些复杂,这个答案没有涉及到。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Mark Amery
@MarkAmery 我真的不确定我看到你在说什么。 - Pointy
@Pointy 根据 OP 的代码,具有 margin-top 的 div 的定位与删除浮动的 div 或将其设置为 position:absolute 时不同。这意味着该效果不能归因于浮动的 div 实际上并不存在于边距计算中。 - Mark Amery
@MarkAmery 嗯,无论出于什么原因,对我来说,OP和我的答案都不能作为片段工作。这可能是由于顶部边距“折叠”的奇怪影响所涉及的。 - Pointy
@MarkAmery 是的,StackExchange 网站出了一些问题。无论如何,在阅读了 W3C 规范关于 clear 的说明后,我相当确定它是一个边距折叠效应。然而,我并不完全理解它。我在答案中进行了一些编辑。 - Pointy
显示剩余6条评论

23

虽然 Pointy 展示了如何使用 div 包裹浮动元素,但是你也可以在浮动元素和主数据区块之间插入一个空的 div。比如:

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both;"></div>
<div style="margin-top: 200px;">Main Data</div>

在某些情况下,添加 div 包装器可能不是理想的解决方案,这时候这个方法可能会有用。


这是一个非常聪明的方法。您还可以将 <div style="clear: both;"></div> 的高度设置为您将要给主数据 <div>margin-top - Ekundayo Blessing Funminiyi

22
这个规范背后的逻辑非常复杂,涉及到清除浮动外边距折叠规则的复杂交互作用。
你可能熟悉传统的CSS 盒子模型,其中内容框包含在填充框中,再被包含在边框框中,最后包含在外边距框中:

Diagram of box model

对于clear设置为除none之外的元素,该模型可能会引入额外的组件:清除

除“none”以外的值可能会引入清除。清除抑制了边距折叠,并作为元素上方的margin-top的间距。

换句话说,在这些情况下,盒模型实际上看起来更像这样:

Box model with 'clearance' added above the top of the margin box

但是什么时候引入间隔,它应该有多大?让我们从第一个问题开始。规范says
计算已设置“clear”的元素的间隔是通过首先确定元素顶部边框边缘的假设位置来完成的。如果元素的“clear”属性为“none”,则此位置是实际顶部边框边缘将在其上的位置。
如果元素的顶部边框边缘的这个假设位置没有超过相关浮动,则引入间隔,并根据8.3.1中的规则折叠边距。
让我们将这个逻辑应用于提问者的代码。记住,我们正在尝试解释下面代码中第三个div的位置(添加背景以帮助可视化):

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: both; margin-top: 200px; background: blue;">Main Data</div>

让我们按照规范的要求,想象一下如果第三个div上的clear属性设置为none而不是both,那么以上代码片段会变成什么样子?

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: none; margin-top: 200px; background: blue;">Main Data</div>

在这里,第三个div覆盖了两个浮动的div。但是请等一下,为什么会这样呢?当然,允许浮动元素重叠块级元素(根据浮动规范“由于浮动不在流程中,非定位块框在浮动框之前和之后创建,垂直地像浮动不存在一样流动。”),但是我们的第三个div有很多margin-top,并且出现在两个浮动的div之后;难道两个浮动的div不应该出现在body的顶部,而第三个div在它们下面200px吗?
这种情况不会发生的原因是第三个div的margin与div的父元素的margin合并(在本例中为body-但是如果您将所有三个div包装在父div中,则会发生相同的行为)。合并margin规范(以下引用省略了几个不相关的细节)告诉我们:
相邻的垂直外边距会合并... 当且仅当以下条件成立时,两个外边距才是相邻的: - 它们都属于参与同一块级格式化上下文的内联块级框。 - 没有行框、清除、填充和边框将它们分开... - 它们都属于垂直相邻的框边缘,即形成以下一对之一: - 盒子的顶部外边距和其第一个内联元素的顶部外边距 - ...
在我们示例中的第三个 div 明显不是 body 的第一个子元素,但它是其第一个内联子元素。请注意,根据 https://www.w3.org/TR/CSS22/visuren.html#positioning-scheme: 元素如果浮动、绝对定位或是根元素,则称为“流外元素(out of flow)”;否则称为“流内元素(in-flow)”。

在我们的例子中,第一个和第二个 div 元素都被浮动了,因此只有第三个 div 元素是 in-flow 的。因此,它的顶部 margin 与其父元素的顶部 margin 相邻,从而使 margin 合并 - 推动整个 body,包括这两个浮动元素。因此,即使第三个 div 元素有一个较大的 margin-top,它仍然会与其兄弟元素重叠。因此,在这种假设情况下,第三个元素的 clear 设置为 none,我们满足以下条件:

元素的顶部边框边缘没有超过相关浮动元素

因此:

引入了间隙,并根据 8.3.1 中的规则合并了 margin

这里引入了多少间隙?规范给浏览器提供了两个选项,并附有一些澄清说明:

然后,间隙的量被设置为以下两者中的较大值: 1. 将块的边框边缘与要清除的最低浮动元素的底部外边缘对齐所需的间隙量。 2. 使块的顶部边框边缘处于其假设位置所需的间隙量。 另外,可以将间隙精确地设置为将块的边框边缘与要清除的最低浮动元素的底部外边缘对齐所需的间隙量。 注意:这两种行为都是允许的,但需要评估它们与现有网络内容的兼容性。未来的 CSS 规范将要求其中之一。 注意:间隙可以是负数或零。
在我们开始应用这些规则之前,我们立即遇到了一个复杂情况。还记得我们在假设的情况下必须考虑的折叠边距吗,当清除为none时?嗯,它在我们计算要使用的间隙时不存在于这个非假设情况中,因为间隙的存在会抑制它。回想一下早先引用的8.3.1中的折叠边距规则,规定边距仅在以下情况下相邻:没有行框、没有间隙、没有填充和没有边框分隔它们(强调添加)。因此,第三个div的上边距和其父级的上边距不再相邻。我们可以通过在body中保持clear:none但是添加padding-top: 1px来模拟此预间隙场景,在此规则下也禁用了边距折叠。

body {
  padding-top: 1px;
}
<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: none; margin-top: 200px; background: blue;">Main Data</div>

现在,与边距合并时不同的是,我们的第三个div舒适地位于其两个浮动兄弟下方。但是,我们已经根据假设情况(边距合并)决定必须添加间隙;剩下的就是选择间隙的数量,以便实现以下目标:

将块的边框边缘放置在要清除的最低浮动块的底部外边缘上

因此,我们别无选择,只能对第三个div应用负间隙,以使其顶部边框边缘上移,接触到上面的浮动元素的底部外边缘(也称为边距边缘)。因此,如果每个浮动元素高度为10px,第三个div具有200px的顶部边距,则将应用-190px的间隙。这样,最终实现了问题提出者所看到的最终结果。

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: both; margin-top: 200px; background: blue;">Main Data</div>

请注意,如果您使用浏览器的开发工具检查上面代码段中的第三个div,您仍然可以看到该div上方的200px顶部间距,超出所有其他内容 - 只是整个边距框已经被大的负间隙向上拖动。
简单!

这个 - 嗯,我本来想说“很有道理”,但那不完全正确- 绝对是准确的故事。我认为,即使没有做这么多工作,我也能得出结论,即没有办法引入一个没有布局影响(0像素“厚度”)的“边距折叠栅栏”。是这样吗? - Pointy
对于容器/子元素折叠的情况,根据规范 https://www.w3.org/TR/CSS22/box.html#collapsing-margins 中的重要提示以及像 https://dev59.com/WW025IYBdhLWcg3wHSGn#6204990 这样的热门答案,父容器上的 overflow: autooverflow: hidden 应该可以解决问题(尽管由于某种原因,如果容器是 body 元素,则无法正常工作)。我不太确定为什么这会起作用,因为它取决于“块格式化上下文”的定义(我还没有研究过并且目前不理解)。 - Mark Amery
@Pointy 使用例如 0.02 像素的栅栏似乎也能够正常工作(至少在 Chrome 中 - 可能是浏览器特定的),尽管对布局没有可见影响。然而,像 0.01px 这样更小的栅栏却不起作用。为什么?没有头绪。 - Mark Amery
是的,我还记得在解决类似问题时尝试过越来越小的边框宽度等方法,直到我受够了并决定我并不是那么在意。这确实是一组令人惊讶的行为。 - Pointy
根据规范:“建立新块格式化上下文的元素(如浮动元素和具有“overflow”属性但不是“visible”的元素)的边距不会与其内部流子元素折叠。”至于为什么要这样做(就像从CSS2到CSS2.1的许多其他BFC相关更改一样),我不确定,但我有一个合理的猜测。它似乎不能与body元素一起使用的原因是当html具有overflow:visible时,body元素会失去其overflow属性。 - BoltClock

7
<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="float: left; clear: both; margin-top: 200px;">Main Data</div>

如果你将第三个元素设置为 "float: left;" 和 "clear: both;",那么它应该会产生所期望的效果,即给第三个元素一个 200 像素的外边距。这里有一个链接提供了一个实例。
这也可能会影响其他后续元素是否需要浮动。但是,这也可能会产生所期望的效果。

这会在第三个 div 后面搞乱下一个元素。最后需要清除浮动。 - Jonas Lundman

4

替代方案:

您实际上可以在浮动元素上放置一个 margin-bottom,以向下推动具有 clear: both 的下面的元素。

http://jsfiddle.net/9EY4R/

enter image description here

注意:尽管我提出了这个建议,但我必须立即撤回它,因为这不是一般的好主意,但在某些有限的情况下可能是合适的;


<div class='order'>

    <div class='address'>
        <strong>Your order will be shipped to:</strong><br>
        Simon</br>
        123 Main St<br>
        Anytown, CA, US
    </div>

    <div class='order-details'>
        Item 1<br>
        Item 2<br>
        Item 3<br>
        Item 4<br>
        Item 5<br>
        Item 6<br>
        Item 7<br>
        Item 8<br>
        Item 9<br>
        Item 10<br>
    </div>

    <div class='options'>
        <button>Edit</button>
        <button>Save</button>
    </div>
</div>

面板上的项目称为 order-details, 其CSS如下。
.order-details
{
    padding: .5em;
    background: lightsteelblue;

    float: left;
    margin-left: 1em;

    /* this margin does take effect */
    margin-bottom: 1em;
}

在上面的代码示例中,黄色面板有一个margin-top属性,但是除非它大于最高的浮动项目,否则它不会起作用(当然这就是这个问题的重点)。
如果您将黄色面板的margin-top属性设置为20em,则它将可见,因为边距是从外部蓝色框的顶部计算的。

3

请在主数据 div 中使用 'padding-top',或者将主数据 div 包装在一个具有 'padding-top' 的 div 中。


0

尝试在其中一个浮动元素上设置底部边距。 或者,您可以将浮动元素包装在父元素中,并使用CSS hack 清除它而不需要其他标记


0
有时候,相对定位和外边距的组合可以解决这些问题。
我在WordPress中的alignright和alignleft类中使用这种技术。
例如,如果我想要一个被清除元素尊重的“底部外边距”,您可以使用以下方法。
.alignright{
   float: right;
   margin-left: 20px;
   margin-top: 20px;
   position: relative;
   top: -20px;
}

针对您的示例,您可以这样做

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both; margin-bottom: 200px; position: relative; top: 200px;">Main Data</div>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接