【问题标题】:Last margin / padding collapsing in flexbox / grid layoutflexbox/网格布局中的最后一个边距/填充折叠
【发布时间】:2016-12-23 21:46:15
【问题描述】:

我有一个项目列表,我试图用 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>

【问题讨论】:

    标签: html css flexbox css-grid


    【解决方案1】:

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

    它在flex-flow: row(水平)、justify-content: space-around(您的边距)上中继,仅此而已! width 更改为 1200px,因为 90px 的填充将框的总宽度设置为超过 600px(在您的 sn-p 中定义)。

    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,因此不符合我的要求。我还需要能够指定每个项目的边距宽度。
    • 所以你不需要 flexbox。你需要一个正常的布局。您可以使用显示内联块或浮点数和空白来避免换行。但是这个要求和flexbox(弹性盒子)没有关系,你也不需要那个弹性盒子,你需要一个固定流动的布局。
    • 我使用 flexbox 是因为我希望元素的高度一致。我还在更大的视口上换行到不同的行;滚动仅适用于小屏幕。我阅读的规范概述提到折叠顶部和底部边距,但不是左右。第一项的左边距没有折叠,所以我觉得最后一项的 RHS 很奇怪。感谢您的帮助,只是没有回答有关折叠边距的问题。
    • 别担心。每个人都知道他的项目需要什么需求。我不知道你所有的要求。所以我祝你好运,但我删除了这个答案,因为它不是你想要的。这里的最后一个边距工作正常;)在您的代码中,我认为这是总宽度或元素宽度的问题。每个元素的大小至少为 200px,边距为 60px,您有 4 个元素,父框的总宽度为 600px。根本不合适。
    【解决方案2】:

    您的问题不在于利润本身。它是仅标注元素可见内容的滚动条。

    解决这个问题的一个技巧是创建一个占据边距的可见元素

    此解决方案在最后一个孩子上使用伪来处理此问题

    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>

    【讨论】:

      【解决方案3】:

      潜在问题 #1

      最后一个边距没有被折叠。它被忽略了。

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

      这是规范中所说的:

      11.1.1 Overflow: the overflow property

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

      现在我们来看看CSS Box Model

      来源:W3C

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


      潜在问题 #2

      潜在问题 #1 的问题在于它似乎在 flex 或 grid 格式化上下文之外崩溃了。例如,在标准块布局中,最后一个边距似乎不会折叠。所以也许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 Block-level, non-replaced elements in normal flow

      以下约束必须保持在其他使用的值之间 属性:

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

      如果width 不是autoborder-left-width + padding-left + width + padding-right + border-right-width(加上任何 margin-leftmargin-right 不是 auto) 大于 包含块的宽度,然后是任何 auto 值 对于以下规则,margin-leftmargin-right 被处理 为零。

      如果以上所有的计算值都不是auto,则这些值被称为“过度约束”并且使用的值之一 必须与其计算值不同。如果direction 包含块的属性值为ltr,指定 margin-right 的值被忽略,该值被计算为 使等式成立。 如果direction 的值为rtl, 这发生在margin-left 身上

      (强调)

      因此,根据CSS Visual Formatting Model,元素可能被“过度约束”,因此右边距被丢弃。


      可能的解决方法

      在最后一个元素上使用右border,而不是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>

      另一种解决方案使用伪元素代替边距或填充。

      弹性容器上的伪元素被渲染为弹性项目。容器中的第一项是::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>

      【讨论】:

      • 注意:如果您在每个元素上使用明确的width 并将border-right 添加到最后一个元素,这将影响该元素作为边框的可见宽度是元素的一部分,如果border-box 是盒子大小。将 box-sizing 更改为默认值或content-box
      • border-left: 10vw solid transparent; 解决了我所有的烦恼,谢谢!
      【解决方案4】:

      你可以在div容器上设置widthoverflow,在ul上设置display: inline-flex而不是flex,这样flex box的大小就会根据里面的项目,所有填充和边距都将应用而没有任何问题。

      .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>

      【讨论】: