子元素的边距会影响父元素的位置

514

我有一个包含另一个

child)的
parent)。 Parent是中没有特定CSS样式的第一个元素。 当我设置

.child
{
    margin-top: 10px;
}
最终结果是我的子元素顶部仍与父元素对齐,而不是子元素向下移动10像素,而是父元素向下移动了10像素。
我的 DOCTYPE 设置为 XHTML Transitional。
我在这里错过了什么?
编辑1: 我的父元素需要有严格定义的尺寸,因为它有一个必须从顶部到底部显示的背景(完美像素)。 因此,在其上设置垂直边距是不可行的。
编辑2: 此行为在 FF、IE 和 CR 上都相同。

248
这种行为毫无意义。边距应该保持在父元素内部,而不是移动父元素。谁写的这些规则? - Muhammad Umer
20
上一条评论加2分。这个“规则”真的让我感到不爽。“60%的时间它每次都能成功” - 这就是利润率。 - Casey Dwayne
44
我完全赞同最后两位评论者的观点。这太疯狂了。有趣的是,在父级元素上添加1像素的边框可以使它正常工作,但这意味着你必须要有一个边框...如果这是预期的行为,那么这太荒谬了。 - erdomester
1
这也可能对你有所帮助 :) https://dev59.com/HZPfa4cB1Zd3GeqPKPm_ - Bhojendra Rauniyar
9
这让我想起了默认的盒模型规则:“我们需要寄送一个盒子。盒子的尺寸不能超过100厘米。为了确保我们的内容在运输过程中不会损坏,我们需要在盒子内部留出10厘米的填充空间。那我们把盒子做成120厘米吧!” 真是个笑话。 - Nolonar
显示剩余4条评论
18个回答

599

Child elements with margins within DIVs 找到了一个替代方案。你还可以添加:

.parent { overflow: auto; }

或:
.parent { overflow: hidden; }

这可以防止边距折叠。边框和填充也具有相同的功能。 因此,您还可以使用以下内容来防止顶部边距折叠:

.parent {
    padding-top: 1px;
    margin-top: -1px;
}

2021更新:如果您愿意放弃对IE11的支持,您还可以使用新的CSS结构display: flow-root。 有关块格式化上下文的完整详细信息,请参见MDN Web Docs


按照普遍要求更新: 折叠边距的整个重点在于处理文本内容。例如:

h1, h2, p, ul {
  margin-top: 1em;
  margin-bottom: 1em;
  outline: 1px dashed blue;
}

div { outline: 1px solid red; }
<h1>Title!</h1>
<div class="text">
  <h2>Title!</h2>
  <p>Paragraph</p>
</div>
<div class="text">
  <h2>Title!</h2>
  <p>Paragraph</p>
  <ul>
    <li>list item</li>
  </ul>
</div>

因为浏览器会折叠外边距,所以文本显示如你所预期,且 <div> 包装标签不影响外边距。每个元素都确保周围有间距,但不会重复计算间距。 <h2><p> 的外边距不会相加,而是互相滑动(它们会折叠)。 <p><ul> 元素也是如此。
可悲的是,在现代设计中,当您明确想要一个容器时,这个想法可能会对您产生影响。在 CSS 中,这被称为新的 块级格式化上下文。使用 overflow 或 margin 技巧即可实现。

54
我会尽力进行翻译,以下是需要翻译的内容:我想知道为什么会发生这种情况,对任何人有什么好处。这个“特性”原本是为哪种情况设计的。 - Muhammad Umer
4
@MuhammadUmer,这与文本内容有关。例如,使用一系列的<h2><p><ul>标签就不会出现奇怪的间隙或“双倍”边距。 - vdboor
7
你能举个例子吗?比如说我有一个div作为背景,里面有h1、h2和p元素。现在我想把h1往下移动一点,这样它就不会靠近背景div的上边缘了,但是又不想扩大h1元素本身。我想到的是添加顶部边距,这很明显。但是当我这样做时,那个美妙的背景也跟着向下移动了。请帮我理解这是一个特性,它如何帮助调整h1、h2和p之间的间距。 - Muhammad Umer
3
我希望它能增加更多的页边距。在我的开发生涯中,折叠外边距是最让人烦恼的事情。如果我将两个 div 放在一起,使用 margin: 5px 10px; ,我期望这两个 div 之间有 20px 的距离,而不是只有 10px 。如果我想要它们之间有 10px 的距离,我会指定 margin: 5px 5px; 。我真的希望有一个根规则可以停止所有外边距的折叠。我讨厌我必须在纸上将边距的大小加倍,才能让它们在屏幕上做出我想要的效果。而这个顶部的东西... 真是一场灾难。 - JL Griffin
3
这刚刚花费了我半天的时间,这是我认为一个不错的解释 https://www.smashingmagazine.com/2019/07/margins-in-css/#parent-first-last-child-element。`display: flow-root` 看起来像是另一种选择。 - peterc
显示剩余8条评论

65

这是正常行为(至少在浏览器实现中是如此)。除非父元素具有填充,否则margin不会影响子元素相对于其父元素的位置,大多数浏览器将子元素的margin添加到父元素的padding中。

要获得所需的行为,您需要:

.child {
    margin-top: 0;
}

.parent {
    padding-top: 10px;
}

4
给父元素添加边框会影响子元素的外边距,可以尝试使用.parent {border: solid 1px;} - o.k.w
2
边距不会加到填充内...它是分开应用的。但视觉效果是一样的。 - Brilliand
2
如果你想要一个顶部边距怎么办?这并不是一个解决方案。 - feeela
4
就像feeela所说的那样,如果需要顶部边距,这并不是一个真正的解决方案。vdboor的答案更适合。 - Joey Morani
1
有很多解决方案,你只需要足够有创意。干杯。 :-) - Slavik Meltser
显示剩余2条评论

30

虽然所有的答案都解决了问题,但它们都伴随着权衡/调整/妥协,例如:

  • floats,您必须浮动元素
  • border-top,这会使父级元素向下至少1像素,然后应该通过向父元素本身引入-1px外边距进行调整。当父元素已经具有相对单位的margin-top时,这可能会创建问题。
  • padding-top,与使用border-top具有相同的效果
  • overflow: hidden,不能用于需要显示溢出内容的父元素,例如下拉菜单
  • overflow: auto,为有意超出内容(如阴影或工具提示的三角形)的父元素引入滚动条

可以通过使用CSS3伪元素来解决此问题,如下所示

.parent::before {
  clear: both;
  content: "";
  display: table;
  margin-top: -1px;
  height: 0;
}

https://jsfiddle.net/hLgbyax5/1/


1
“margin-top: -1px” 似乎是不必要的。但我喜欢它。 - Flimm
4
实际上,在Chrome中测试,margin-top: -1pxheight: 0似乎都是不必要的。但这是最好的解决方案。 - NinjaFart
这种方法大多数情况下是有效的,但不会尊重容器中最后一个元素的底部边距。它并不像overflow: hidden;一样完美解决问题。 - Michael Giovanni Pumo
1
@MichaelGiovanniPumo 可以通过使用 .parent:after... 来修改答案,使其适用于最后一个元素的底部边距。 - Ejaz
这绝对是现在的正确答案。它完美无缺地运行(放在之前或之后都可以),我希望人们敢于忽略其他的回答! - fgblomqvist

25

将样式display:inline-block添加到子元素


14

父元素不能是空的,至少要在子元素前加上&nbsp;


4
是的,有时候只有这样才能帮助……或者更好的说法是这样的:<div style='width:0;height:0'> </div> - Pigalev Pavel

9

5
为了避免"Div父级"使用"Div子级"的margin,您可以在父级中使用以下CSS:
  • 浮动(float)
  • 填充(padding)
  • 边框(border)
  • 溢出(overflow)

3

简洁的纯CSS解决方案

使用以下代码向意外移动的div前添加一个没有内容的:first-child:

.parent:before
{content: '';position: relative;height: 0px;width: 0px;overflow: hidden;white-space: pre;}

这种方法的优点在于您不需要更改任何现有元素的CSS,因此对设计的影响最小。此外,添加的元素是一个伪元素,它在DOM树中。
伪元素的支持非常广泛:Firefox 3+、Safari 3+、Chrome 3+、Opera 10+和IE 8+。这将在任何现代浏览器中工作(要注意较新的::before在IE8中不受支持)。

背景

如果元素的第一个子元素具有margin-top,则父元素将调整其位置以折叠冗余的边距。为什么?就是这样。
考虑以下问题:
<style type="text/css">
div {position: relative;}
.parent {background-color: #ccc;}
.child {margin-top: 40px;}
</style>

<div class="parent"><!--This div moves 40px too-->
    <div class="child">Hello world!</div>
</div>

您可以通过添加内容的子元素来解决此问题,例如一个简单的空格。但我们都不喜欢为仅是设计问题而添加空格。因此,请使用 white-space 属性来模拟内容。
<style type="text/css">
div {position: relative;}
.parent {background-color: #ccc;}
.child {margin-top: 40px;}
.fix {position: relative;white-space: pre;height: 0px;width: 0px;overflow: hidden;}
</style>

<div class="parent"><!--This div won't move anymore-->
    <div class="fix"></div>
    <div class="child">Hello world!</div>
</div>

position: relative;确保修复位置正确。 white-space: pre;使您不需要添加任何内容(如空格)到修复。 height: 0px;width: 0px;overflow: hidden; 确保您永远看不到修复。

您可能需要添加line-height: 0px;max-height: 0px;以确保在古老的IE浏览器中高度实际上为零(我不确定)。如果不起作用,您还可以在旧版IE浏览器中将<!--dummy-->添加到其中。

简而言之,您只需使用CSS即可执行所有这些操作(这消除了向HTML DOM树添加实际子元素的必要性):

<style type="text/css">
div {position: relative;}
.parent {background-color: #ccc;}
.child {margin-top: 40px;}

.parent:before {content: '';position: relative;height: 0px;width: 0px;overflow: hidden;white-space: pre;}
</style>

<div class="parent"><!--This div won't move anymore-->
    <div class="child">Hello world!</div>
</div>

只有在你非常绝望,而且无法为现有元素设置任何自定义CSS时,才使用此解决方案 - 否则请使用真正的解决方案:为父元素设置 overflow: hidden; - Yeti

3

玩转父元素的显示

.parent{
     display: inline-block;
     width: 100%;
}

或者
.parent{ display: flex; }

2
我发现,在您的 .css 文件中,如果您将 div 元素的 显示属性 设置为 inline-block ,它可以解决问题,而且 margin 会按预期工作。

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