弹性盒子/网格布局中的最后一个边距/内边距合并

52
我有一个项目列表,想要使用flexbox将其排列成可滚动的水平布局。
容器中的每个项目都有左右边距,但是最后一个项目的右边距会被折叠。
有没有办法阻止这种情况发生,或者有什么好的解决方法?

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 300px;
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  margin: 0 30px;
  white-space: nowrap;
  flex-basis: auto;
}
<div class="container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>

5个回答

85

潜在问题#1

最后一个边距没有被折叠,而是被忽略了。

overflow 属性仅适用于内容,而不适用于填充或边距。

规范中的说明如下:

11.1.1 溢出: overflow属性

此属性指定当块级容器元素的内容溢出元素的框时是否裁剪该内容。

现在让我们看一下CSS Box Model

enter image description here

来源: W3C

overflow 属性仅限于内容框区域。如果内容溢出其容器,则应用 overflow。但是 overflow 不会进入填充或边距区域(除非当然有更多的内容跟随)。


潜在问题 #2

潜在问题 #1 的问题在于它似乎只适用于弹性盒子或网格布局环境。例如,在标准块布局中,最后一个边距似乎不会折叠。因此,也许允许 overflow 覆盖边距/填充,无论规范中说什么。

div {
  height: 150px;
  overflow: auto;
  width: 600px;
  background: orange;
  white-space: nowrap;
}
span {
  background: blue;
  color: #fff;
  padding: 50px;
  margin: 0 30px;
  display: inline-block;
}
<div class="container">
    <span>Item 1</span>
    <span>Item 2</span>
    <span>Item 3</span>
    <span>Item 4</span>
</div>

因此,问题可能与“过度约束”的元素有关。

10.3.3 正常流中的块级非替换元素

其他属性的使用值必须遵循以下限制:

margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right = 包含块的宽度

如果 width 不是 auto,且 border-left-width + padding-left + width + padding-right + border-right-width(加上任何不是 automargin-leftmargin-right)大于包含块的宽度,则对于以下规则,任何 margin-leftmargin-rightauto 值将被视为零。

如果上述所有值的计算值均不为 auto,则这些值被称为“过度约束”,其中一个使用值必须与其计算值不同。如果包含块的 direction 属性具有值 ltr,则忽略 margin-right 的指定值,并计算该值以使等式成立。 如果 direction 的值为 rtl,则会对 margin-left 进行相同的处理

(已加重)

根据CSS可视化格式模型,元素可能会被“过度约束”,因此右边距会被抛弃。


可能的解决方法

可以在最后一个元素上使用右侧的 边框,而不是使用 margin 或 padding:

li:last-child {
  border-right: 30px solid orange;
}

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 100px; /* adjusted for demo */
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  margin: 0 30px;
  white-space: nowrap;
  flex-basis: auto;
}
li:last-child {
  border-right: 30px solid orange;
}
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
</ul>

另一种解决方案使用伪元素而非边距或填充。
在 flex 容器上的伪元素被渲染为 flex 项。容器中的第一项是 ::before,最后一项是 ::after。
ul::after {
  content: "";
  flex: 0 0 30px;
}

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 100px; /* adjusted for demo */
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  margin: 0 30px;
  background: blue;
  color: #fff;
  padding: 90px;
  white-space: nowrap;
  flex-basis: auto;
}
ul::after {
  content: "";
  flex: 0 0 30px;
}

ul::before {
  content: "";
  flex: 0 0 30px;
}
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
</ul>


1
注意:如果您在每个元素上使用明确的“宽度”并将“border-right”添加到最后一个元素,则这将影响该元素的可见宽度,因为在box-sizing为“border-box”的情况下,边框是元素的一部分。将box-sizing更改为默认或content-box - Akansh
1
border-left: 10vw solid transparent; 解决了我所有的问题,谢谢! - Alexandre Daubricourt

9

你的问题不是边距本身,而是滚动条仅对元素可见内容进行尺寸调整。

解决这个问题的一个方法是创建一个占据边距的可见元素。

这个方案使用伪类在最后一个子元素上实现。

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 300px;
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  margin: 0 30px;
  white-space: nowrap;
  flex-basis: auto;
  position: relative;
}
li:last-child:after {
  content: "";
  width: 30px;
  height: 1px;
  position: absolute;
  left: 100%;
  top: 0px;
}
<div class"container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>


4
你可以在 div 容器上设置 widthoverflow,并在 ul 上设置 display: inline-flex 而不是 flex,这样 flex 盒子的大小将基于内部项目计算,所有填充和边距都将应用而不会出现问题。

.container {
  width: 600px;
  overflow: auto;
}

.container ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: inline-flex;
  background: orange;
}

.container li {
  padding: 60px;
  margin: 0 30px;
  white-space: nowrap;
  background: blue;
  color: #fff;
}
<div class="container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>


0
截至2023年10月,看起来您可以通过在父元素上使用gappadding的组合来解决这个问题,而不是使用margin的伪元素。

ul {
  list-style-type: none;
  margin: 0;
  display: flex;
  height: 300px;
  overflow: auto;
  width: 600px;
  background: orange;
  /* ADD THIS */
  gap: 60px;
  padding: 0 60px;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  white-space: nowrap;
  flex-basis: auto;
}
<div class="container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>


-2

看解决方案。我删除了 white-spaceflex-basismargin,为您提供了一个纯粹的 flexbox 解决方案。

它依赖于 flex-flow: row(水平方向)、justify-content: space-around(您的 margin)等属性,没有更多的属性需要设置!由于 90px 的 padding 使得盒子的总宽度超过了您在代码片段中定义的 600px,因此将 width 更改为 1200px

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-flow: row ;
  justify-content: space-around;
  height: 300px;
  overflow: auto;
  width: 1200px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
}
<div class"container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>


使用该解决方案,如果容器比列表项的组合宽度更窄,则边距将折叠为0,因此并不符合我的要求。我还需要能够指定每个项目的边距宽度。 - Kevin Wilson
所以你不需要使用flexbox。你需要一个普通的布局。你可以使用display inline-block或者floats,并使用white-space来避免换行。但是这些要求与flexbox(灵活的盒子)无关,你并不需要那种灵活的盒子,而是需要一个固定-流动的布局。 - Marcos Pérez Gude
我使用flexbox是因为我想要元素的高度保持一致。在较大的视口上,我也会换行到不同的行;滚动条只是针对小屏幕的。我阅读的规范概述提到了折叠顶部和底部边距,但没有提到左右边距。第一个项目的左边距没有折叠,所以我觉得最后一个项目的右侧折叠很奇怪。感谢您的帮助,但这并没有回答有关折叠边距的问题。 - Kevin Wilson
别担心,每个人都知道他的项目需要什么要求。我不知道你所有的要求。所以祝你好运,但是我删除了这个答案,因为它不是你想要的。这里的最后一个边距很好用 ;) 在你的代码中,我认为问题在于总宽度或元素的宽度。每个元素的尺寸至少为200像素,间距为60像素,你有4个元素,父盒子的总宽度为600像素。简单来说,它不适合。 - Marcos Pérez Gude

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