具有“溢出:隐藏”溢出祖父母边距的 Flexbox 子项

Posted

技术标签:

【中文标题】具有“溢出:隐藏”溢出祖父母边距的 Flexbox 子项【英文标题】:Flexbox child with "overflow: hidden" overflowing grandparent margins 【发布时间】:2019-11-23 01:17:14 【问题描述】:

我正在尝试将两个子元素嵌套在一个包装器中,该包装器指定侧边距,以便在显示窄时其内容和屏幕两侧之间有空间,而当显示宽时使用max-width

第二个孩子有一些溢出应该是可见的,而第一个孩子应该严格留在包装器的内容框中。删除第一个孩子后,第二个孩子的行为符合预期。但是,当我添加第一个孩子时,它似乎完全忽略了包装器的边距,拉伸了包装器的内容框并打破了第二个孩子。

overflow: hidden 应用于包装器可以解决边距问题,但会剪切第二个孩子。将边距应用到第一个子级并不会使其与父级一起折叠,因为它位于新的块格式化上下文中。

到目前为止,我发现的唯一解决方法是:

.wrapper 
    > * 
        margin-left: 1.5rem;
        margin-right: 1.5rem;
    

并将包装器的最大宽度增加 3rem,但我希望有一些解决方案不需要我将边距从包装器转移到其子级。

https://codepen.io/HybridCore/pen/jjoWmd

body 
  background-color: #000;
  color: #FFF;
  display: flex;
  justify-content: center;


.wrapper 
  margin: 0 1.5rem;
  max-width: 40rem;
  width: 100%;


.fit_content_box 
  display: flex;
  align-items: center;


.L 
  min-width: 0;
  flex: 1 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;


.R 
  margin-left: 1rem;
  height: 1rem;
  width: 1rem;
  background-color: #FFF;


.overflow 
  display: flex;
  justify-content: space-between;


.overflow>div 
  width: 0;
  display: flex;
  justify-content: center;
<body>
  <div class="wrapper">
    <div class="fit_content_box">
      <p class="L">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
        dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

      <div class="R"></div>
    </div>

    <div class="overflow">
      <div>
        <p>0</p>
      </div>
      <div>
        <p>12</p>
      </div>
      <div>
        <p>24</p>
      </div>
    </div>
  </div>
</body>

【问题讨论】:

@Michael_B,为什么要设置导致问题的width: 0? codepen.io/tarunlalwani/pen/voNGNw @TarunLalwani,这并不能解决问题。请参阅以下 Temani Afif 的答案。 【参考方案1】:

你主要有两个问题:

    您将 width:100% 设置为包装器,这不考虑边距,因此您在逻辑上会溢出,并且由于主体是带有 justify-content:center 的 flex 容器,边距将从两侧平均溢出,这就是您认为的原因未应用。 您正面临the min-width constraint of flexbox,这迫使您设置width:100%,认为这是一个很好的解决方案。同样的约束也可以防止元素收缩到低于您指定的 100%(相关:Why is a flex item limited to parent size?)

要解决此问题,您需要从包装器中删除 width:100% 并改为考虑使用 min-width:0。也可以去掉min-width应用于.Lflex-shrink:0.R(或将其宽度替换为min-width

body 
  background-color: #000;
  color: #FFF;
  display: flex;
  justify-content: center;


.wrapper 
  margin: 0 1.5rem;
  max-width: 40rem;
  min-width:0;


.fit_content_box 
  display: flex;
  align-items: center;


.L 
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;


.R 
  margin-left: 1rem;
  flex-shrink:0;
  height: 1rem;
  width: 1rem;
  background-color: #FFF;


.overflow 
  display: flex;
  justify-content: space-between;


.overflow>div 
  width: 0;
  display: flex;
  justify-content: center;
<body>
  <div class="wrapper">
    <div class="fit_content_box">
      <p class="L">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
        dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

      <div class="R"></div>
    </div>

    <div class="overflow">
      <div>
        <p>0</p>
      </div>
      <div>
        <p>12</p>
      </div>
      <div>
        <p>24</p>
      </div>
    </div>
  </div>
</body>

如果您希望元素在有少量文本时至少保持等于max-width,请添加flex-grow:1

body 
  background-color: #000;
  color: #FFF;
  display: flex;
  justify-content: center;


.wrapper 
  margin: 0 1.5rem;
  max-width: 40rem;
  min-width:0;
  flex-grow:1;


.fit_content_box 
  display: flex;
  align-items: center;


.L 
  flex-grow:1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;


.R 
  margin-left: 1rem;
  flex-shrink:0;
  height: 1rem;
  width: 1rem;
  background-color: #FFF;


.overflow 
  display: flex;
  justify-content: space-between;


.overflow>div 
  width: 0;
  display: flex;
  justify-content: center;
<body>
  <div class="wrapper">
    <div class="fit_content_box">
      <p class="L">Lorem ipsum dolor sit e dolor sit e</p>

      <div class="R"></div>
    </div>

    <div class="overflow">
      <div>
        <p>0</p>
      </div>
      <div>
        <p>12</p>
      </div>
      <div>
        <p>24</p>
      </div>
    </div>
  </div>
</body>

为了更好地说明 (1),这里是另一个您几乎不会注意到的边距溢出示例:

.container 
  width:200px;
  margin:auto;
  display:flex;
  justify-content:center;

.box 
  height:50px;
  width:100%;
  background:red;
<div class="container">
  <div class="box" style="margin:0 5966px">a_long_text_to_avoid_the_shrink</div>
</div>

<div class="container">
  <div class="box">a_long_text_to_avoid_the_shrink</div>
</div>

你可以看到我们有一个长文本迫使我们的元素不收缩(最小宽度约束),元素占据全宽并且我们将内容居中。这会使边距溢出,就像没有边距一样。

如果你违反了一条规则,那么你就会看到边距的效果。

删除长文本:

.container 
  width:200px;
  margin:auto;
  display:flex;
  justify-content:center;

.box 
  width:100%;
  height:50px;
  background:red;
<div class="container">
  <div class="box" style="margin:0 5966px">a long text to avoid the shrink</div>
</div>

<div class="container">
  <div class="box">a long text to avoid the shrink</div>
</div>

去除居中:

.container 
  width:200px;
  margin:auto;
  display:flex;

.box 
  width:100%;
  height:50px;
  background:red;
<div class="container">
  <div class="box" style="margin:0 5966px">a_long_text_to_avoid_the_shrink</div>
</div>

<div class="container">
  <div class="box">a_long_text_to_avoid_the_shrink</div>
</div>

在每一边制作不同的边距

.container 
  width:200px;
  margin:auto;
  display:flex;
  justify-content:center;

.box 
  width:100%;
  height:50px;
  background:red;
<div class="container">
  <div class="box" style="margin:0 500px 0 400px">a_long_text_to_avoid_the_shrink</div>
</div>

<div class="container">
  <div class="box">a_long_text_to_avoid_the_shrink</div>
</div>

(2) white-space 正在创建最小宽度约束以防止元素缩小。

这里有一个例子来说明:

.body 
  display: flex;
  justify-content: center;
  margin: 10px 100px;
  border: 2px solid red;


.wrapper 
  border: 1px solid;
  margin: 0 20px;

.box 
 display:flex;
The below is a logical behavior where the text will wrap and the margin are respected
<div class="body">
  <div class="wrapper">
    <div class="box">
      <div>some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>
Let's add white-space:nowrap. We add a min-width contraint since we said to the text to never wrap thus our flex element will not shrink and overflow.
<div class="body">
  <div class="wrapper">
    <div class="box">
      <div style="white-space:nowrap">some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>
If we add width:100% we force its width to be the same as the container BUT the margin aren't included and are kept outside (the text will logically overflow)
<div class="body">
  <div class="wrapper" style="width:100%">
    <div class="box">
      <div style="white-space:nowrap">some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>
Now if we add min-width:0 we remove the constaint of minimum sizing and we can see the margin again even if we keep width:100% because the element will shrink by default
<div class="body">
  <div class="wrapper" style="width:100%;min-width:0">
    <div class="box">
      <div style="white-space:nowrap">some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>

诀窍是我们将元素居中并在两侧应用相同的边距,这会产生边距折叠的错觉,但它只是从两侧平均地溢出边距。

让我们稍微改变一侧的边距,看看另一侧有一点偏移:

.body 
  display: flex;
  justify-content: center;
  margin: 10px 100px;
  border: 2px solid red;


.wrapper 
  border: 1px solid;
  margin: 0 20px 0 40px;

.box 
 display:flex;
The below is a logical behavior where the text will wrap and the margin are respected
<div class="body">
  <div class="wrapper">
    <div class="box">
      <div>some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>
Let's add white-space:nowrap. We add a min-width contraint since we said to the text to never wrap thus our flex element will not shrink and overflow.
<div class="body">
  <div class="wrapper">
    <div class="box">
      <div style="white-space:nowrap">some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>
If we add width:100% we force its width to be the same as the container BUT the margin aren't included and are kept outside (the text will logically overflow)
<div class="body">
  <div class="wrapper" style="width:100%">
    <div class="box">
      <div style="white-space:nowrap">some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>
Now if we add min-width:0 we remove the constaint of minimum sizing and we can see the margin again even if we keep width:100% because the element will shrink by default
<div class="body">
  <div class="wrapper" style="width:100%;min-width:0">
    <div class="box">
      <div style="white-space:nowrap">some long text here some long text here some long text here some long text here</div>
    </div>
  </div>
</div>

【讨论】:

我这样做width: 100% 的原因是,如果空间允许,包装器将至少占据最大宽度。如果 .L 的内容没有溢出,则包装器在宽度上折叠。这是一个示例笔:codepen.io/HybridCore/pen/rXxaby 基本上我希望包装器在视口允许的情况下保持最大宽度,无论 .L 是否有足够的文本将包装器推到最大宽度。 @HybridCore 很简单,只需添加flex-grow: 1; 让它成长,不要使用width:100% 一个经典的min-width默认问题(which I've written about so much!)。我完全忽略了 body 元素上的 flex 容器。感谢您抽出宝贵的时间。很好的答案。【参考方案2】:

问题的根源似乎是white-space: nowrap,它应用于第一个子项(.fit_content_box)内的内容元素(.L)。

.L 
    border: solid 1px #FF0000;
    min-width: 0;
    flex: 1 0;
    overflow: hidden;
    white-space: nowrap;    <--- trouble maker
    text-overflow: ellipsis;

如果您删除该行代码,.wrapper 上的边距将按预期工作。

所以关键问题是:

为什么孙子 (.L) 上的 white-space 属性会折叠祖父 (.wrapper) 的边距? 为什么white-space 属性在应用于父级 (.fit_content_box) 时会折叠侧边距? 为什么 overflow 属性在应用于祖父 (.wrapper) 时,其值不是 visible,允许边距在孙子 (.L) 上保持稳定?

你写道:

将页边距应用到第一个子级并不会使其与父级一起折叠,因为它位于新的块格式化上下文中。

实际上,这不是传统的边距折叠问题,因为:

    我们谈论的是水平边距,horizontal margins never collapse,以及

    我们在弹性容器中工作,margins inside a flex container never collapse。

因此,尽管对问题的全面理解可能在于块(或 flex)格式化上下文,但我不确定这就是为什么父级上的边距不会折叠的原因。

这就是我对这个问题的了解。有时间我会做更多的研究。或者也许其他人可以从这里取走它。

【讨论】:

是的,这很好。我再次检查了你的问题并修改了我的答案。 标题已更改以反映您的答案。我尝试进行更多测试,但没有得到任何结果。奇怪的是,它似乎只尊重其前身元素的边距,如果它是 body 元素而不是其他元素(你最初关于应用边距的建议)。不确定这是实现错误、规范中的错误还是预期行为。 我的原始答案建议 padding,而不是 body 元素上的 margin。我敢打赌,这是预期的行为。可能是一个错误,但主要浏览器的行为是一致的(我测试了 Chrome、FF 和 Edge)。 添加了答案 如果您愿意,请随时接受@TemaniAfif 的回答。他解开了这个谜。

以上是关于具有“溢出:隐藏”溢出祖父母边距的 Flexbox 子项的主要内容,如果未能解决你的问题,请参考以下文章

溢出:隐藏在 Flexbox 中无法按预期工作

非常简单的 flexbox 布局,带有溢出:隐藏——Firefox 中的垂直错位

flexbox 边距导致溢出

边界半径和溢出:隐藏(离子应用程序)

Flexbox溢出-x最后边距[重复]

Flexbox溢出-x最后一个边距[重复]