【问题标题】:Unexpected result from margin-top next to a floated element浮动元素旁边的 margin-top 出现意外结果
【发布时间】:2019-12-13 22:16:20
【问题描述】:

我遇到了这样的布局问题:

.wrapper {
  clear: both;
  background-color: #ccc;
}

.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  margin-top: 20px;
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  width: 100px;
  float: left;
  background: lightblue;
}

.main {
  margin-left: 100px;
  background: lightgreen;
}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

<div class="wrapper">
  <div class="top">
    <div class="box">top</div>
  </div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

.main.side 元素需要对齐。正如您在上面的 sn-p 中看到的,一切都很好,除非 .top 元素没有高度,在这种情况下 margin-top 规则会导致它们倾斜。以下所有“修复”问题,但每个都有一个缺点:

  • .wrapper添加边框(我可能可以使用透明边框,但我真的不喜欢这样,因为它感觉像一个肮脏的黑客,我宁愿不添加边框。出于某种原因边框需要有至少 1px 的宽度,否则这不起作用)

.wrapper {
  clear: both;
  background-color: #ccc;
  border: 1px solid #000;
}

.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  margin-top: 20px;
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  width: 100px;
  float: left;
  background: lightblue;
}

.main {
  margin-left: 100px;
  background: lightgreen;
}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>
  • overflow: hidden 添加到.wrapper(这会隐藏某些元素的一部分并导致其他元素落入错误的位置)
  • overflow: auto 添加到.wrapper(这在某些情况下会添加滚动条)

最后两个在我的 sn-p 中并不明显,但在现实世界的应用程序中,它们会导致问题,如 here 所述。

我强烈怀疑该问题与Why doesn't the height of a container element increase if it contains floated elements?CSS container doesn't stretch to accommodate floats 有关,但我已经尝试了许多这些建议,但似乎没有一个可以完全解决问题 - 可能是因为我的一个 div 是浮动的,而另一个是不是。

由于这是一个大型应用程序的一部分,我不想彻底改变布局,只要有一些 CSS 将保持 .main.side 对齐,而不管这些元素之前的内容如何。

【问题讨论】:

  • 为什么不使用 flexbox 呢?甚至 inline-block 都可以完成这项工作
  • @TemaniAfif - 请在下面查看我对符号链接的回答的回复。感谢您的建议,我已经使用了 flex 并且喜欢它——但不幸的是,这不是一个选择。正如我在问题中提到的,我想在不更改布局的情况下解决问题
  • 顺便说一句,问题与页边距折叠有关,其中父页边距与 first 流入子项一起折叠(在您的情况下,这是第二个,因为第一个已出流量)
  • 我对此有一种感觉 - 我只是对如何在布局的上下文中解决它感到困惑。
  • 并且您希望第一个案例中的页边距顶部展开?你想一直看到灰色吗?

标签: html css css-float


【解决方案1】:

您可以将主元素设为inline-block 并使用 calc 设置宽度。这应该不会对您的布局产生太大影响,并且您会得到正确的输出:

.main {
  width:calc(100% - 100px);
  display:inline-block;
  background: lightgreen;
}

完整代码:

.wrapper {
  background-color: #ccc;
  clear: both;
}
.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  margin-top: 20px;
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  width: 100px;
  float: left;
  background: lightblue;
}

.main {
  width:calc(100% - 100px);
  display:inline-block;
  background: lightgreen;
}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

<div class="wrapper">
  <div class="top">
    <div class="box">top</div>
  </div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

另一个 hacky 想法是确保您的顶部元素永远不会为空:

.top:empty {
  font-size:0;
}
.top:empty::before {
  content: "\80"; /* a random character */
}

完整代码

.wrapper {
  background-color: #ccc;
  clear: both;
}

.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  margin-top: 20px;
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  width: 100px;
  float: left;
  background: lightblue;
}

.main {
  margin-left: 100px;
  background: lightgreen;
}

.top:empty {
  font-size:0;
}
.top:empty::before {
  content: "\80"; /* a random character */
}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

<div class="wrapper">
  <div class="top">
    <div class="box">top</div>
  </div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

您也可以考虑相同的技巧,但在主包装器上使用伪元素:

.wrapper::before {
  content: "\80"; /* a random character */
  display:block;
  font-size:0;
}

完整代码

.wrapper {
  background-color: #ccc;
  clear: both;
}

.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  margin-top: 20px;
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  width: 100px;
  float: left;
  background: lightblue;
}

.main {
  margin-left: 100px;
  background: lightgreen;
}

.wrapper::before {
  content: "\80"; /* a random character */
  display:block;
  font-size:0;
}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

<div class="wrapper">
  <div class="top">
    <div class="box">top</div>
  </div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

您还可以使包装器inline-block 的宽度等于 100%,它的行为几乎与块元素相同:

.wrapper {
  background-color: #ccc;
  display:inline-block;
  width:100%;
  vertical-align:top; /* avoid some unwanted white space issue*/
}

.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  margin-top: 20px;
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  width: 100px;
  float: left;
  background: lightblue;
}

.main {
  margin-left: 100px;
  background: lightgreen;
}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

<div class="wrapper">
  <div class="top">
    <div class="box">top</div>
  </div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

为了解释,您正面临the specification 中所述的边距折叠问题:

两个边距相邻当且仅当:

  • 两者都属于参与相同块格式化上下文的流入块级框
  • 没有线框,没有间隙,没有填充,也没有边框将它们分开(请注意,出于此目的,某些零高度线框(请参阅 9.4.2)会被忽略。)
  • 两者都属于垂直相邻的盒子边缘,即形成以下之一:
    • 框的上边距及其第一个流入子元素的上边距

【讨论】:

  • 谢谢 - 我确实尝试过,是的,它有效。我试图避免这样做,因为在现实生活应用程序中有大量的响应规则需要重写以适应。不过,Atm 似乎确实可能是最好的解决方案,而且据我所知,它需要的修改最少。
  • 实际上,包装器上的border: 1px solid transparent; 是侵入性最小的修复程序,但它看起来很奇怪,我不确定为什么会起作用,因此我不愿意使用它
  • @billynoah 因为它禁用边距折叠......如果父子边距之间有任何东西,它们不会折叠(填充或边框或其他元素)
  • @billynoah 添加了另一个想法 ;) 可能会添加更多,因为这有很多技巧
  • 最新的想法实际上让我发笑,但是谢谢,这对我来说是一个新想法
【解决方案2】:

您可以使用网格更优雅地做到这一点。这是网格代码:

.wrapper {
    display: grid;
    grid-template-areas:
        "top top"
        "side main";
    grid-template-columns: 100px 1fr;
}

.top{grid-area:top}
.side{grid-area:side}
.main{grid-area:main}

请注意我能够注释掉多少其他元素并仍然保持所需的布局。

.wrapper {
  /*clear: both;*/
  background-color: #ccc;
}

.wrapper+.wrapper {
  margin-top: 50px;
}

.side,
.main {
  height: 100px;
  padding: 10px;
  box-sizing: border-box;
  /*margin-top: 20px;*/
}

.box {
  padding: 10px;
}

.top {
  background: yellow;
}

.side {
  /*width: 100px;
  float: left;*/
  background: lightblue;
}

.main {
  /*margin-left: 100px;*/
  background: lightgreen;
}

.wrapper {
    display: grid;
    grid-template-areas:
        "top top"
        "side main";
    grid-template-columns: 100px 1fr;
}

.top{grid-area:top}
.side{grid-area:side}
.main{grid-area:main}
<div class="wrapper">
  <div class="top"></div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

<div class="wrapper">
  <div class="top">
    <div class="box">top</div>
  </div>
  <div class="side">side</div>
  <div class="main">main</div>
</div>

【讨论】:

  • 谢谢,我知道grid 并已在其他应用程序中使用它(并且喜欢它!)。但是,正如我在问题中所说,在这里更改布局是不现实的。这是一个庞大的应用程序,有很多复杂性超出了这个问题的范围来解释。可以说,基本的浮动/非浮动布局方法需要保留。
  • btw - 基本网格布局的精彩演示 :-) 为此 +1,让我们希望在 2020 年结束所有浮动。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-01
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多