【问题标题】:Centering grid content while left-aligning each row's content, with arbitrary item/column widths居中网格内容,同时左对齐每一行的内容,具有任意项/列宽度
【发布时间】:2020-10-07 14:05:16
【问题描述】:

我有一排任意宽度的项目。它们在容器内居中(注意红色容器左右两侧的空白区域):

有时容器变得小于所有项目的宽度:

发生这种情况时,我希望最后的项目像这样换行到下一行:

对我来说非常重要的是每一行的内容必须左对齐,但网格作为一个整体必须居中

最初,我尝试使用 FlexBox 来实现它。在经历了很多挫折和拉扯之后,我了解到这在 FlexBox 是不可能的:https://stackoverflow.com/a/32811002/901944

同一页面上的另一个答案建议使用 CSS 网格而不是 flexbox。

CSS 网格产生的结果略有不同,但这也适合我:

下面是使它工作的代码:

.red-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
  justify-content: center;
}

这段代码包含很多我看不懂的关键字:grid-template-columnsrepeatauto-fitminmaxmax-content。我尝试阅读它们但失败了。没有任何指南和 API 文档明确解释这种特定组合的工作原理。 MDN 文档太短太神秘了。

我特别纠结的是这个210px 幻数。为什么有必要? (嗯,我知道这是必要的,因为规范是如何设计的,但这并不能帮助我理解。)

我的网格中项目的大小是任意的,所以我不能使用固定值。此外,设置这个固定值会使结果略微偏离:小项目增长,大项目溢出容器。

我真正想要的是:

  grid-template-columns: repeat(auto-fit, minmax(min-content, max-content));

但该规则被浏览器识别为错误。

我偶然发现了this answer,它解释了在这种情况下,规范禁止同时使用min-contentmax-content。答案建议的解决方案是……使用 Flexbox!

循环已关闭。我回到了我开始的地方,期待我现在头上的头发又少了一轮。

如何在左对齐每行内容的同时将网格居中,并且项目具有任意宽度?

为了方便起见,这里有一个样板文件:https://jsbin.com/vuguhoj/edit?html,css,output

可以通过拖动右下角来调整容器的大小。

请不要display: inlinefloat: left

.page {
  border: 1px solid black;
  overflow: hidden;
  resize: horizontal;
  max-width: 500px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(max-content, 50px));
  justify-content: center;
}

.item {
  border: 1px solid black;
  margin: 1px;
}
<div class="page">
  <div class="grid">
    <div class="item">
      Foofoofoo
    </div>
    <div class="item">
      Bar
    </div>
    <div class="item">
      BazBaz
    </div>
    <div class="item">
      QuuxQuuxQuux
    </div>
  </div>
</div>

【问题讨论】:

  • 你会接受说这是不可能的答案吗?除非您愿意使用 JS,否则很可能是这种情况

标签: css flexbox css-grid


【解决方案1】:

您有两个可用的容器 - .page.grid

这使您能够分配两个任务 - 居中和左对齐。

使用顶级容器进行居中。

使用嵌套容器进行换行和左对齐。

这是一个代码概念:

.page {
  display: grid;
  grid-template-columns: 50px 1fr 50px;
  border: 1px solid black;
  max-width: 500px;
}

.grid {
  grid-column: 2;
  justify-self: center;
  display: flex;
  flex-wrap: wrap;
}

.item {
  border: 1px solid black;
  margin: 1px;
}
<div class="page">
  <div class="grid">
    <div class="item">Foofoofoo</div>
    <div class="item">Bar</div>
    <div class="item">BazBaz</div>
    <div class="item">QuuxQuuxQuux</div>
  </div>
</div>

jsFiddle demo

【讨论】:

  • 不起作用。当容器小于所有元素的总和时,网格内容不居中:jsfiddle.net/4w8n2gyd
  • 请更仔细地编辑问题,您损坏了问题中的一张图片。 :P
  • grid-template-columns 上不要使用0,也许试试1frjsfiddle.net/6zdk891j
  • 已修复。下次请在问题中包含您的代码,这样它就不会被编辑或关闭;-)
  • "在 grid-template-columns 上用 0 代替,试试 1fr 吧?" — 你的grid-template-columns: 1fr 1fr 1fr; 等同于padding-left: 33%; padding-right: 33%;。当还有足够的空间让它们留在一行时,项目会换行到第二行。
【解决方案2】:

如果我们选择center,我们不会得到left-align 的东西:

.page {
  border: 1px solid black;
  max-width: 500px;
  resize: horizontal;
  overflow: hidden;
  display: flex;
  justify-content: center;
  padding: 0 50px;
}

.grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.item {
  border: 1px solid black;
  margin: 1px;
}
<div class="page">
  <div class="grid">
    <div class="item">Foofoofoo</div>
    <div class="item">Bar</div>
    <div class="item">BazBaz</div>
    <div class="item">QuuxQuuxQuux</div>
  </div>
</div>

现在使用left-align(如@Michael Benjamin 的回答)它不会做center 的事情:

.page {
  border: 1px solid black;
  max-width: 500px;
  resize: horizontal;
  overflow: hidden;
  display: flex;
  justify-content: center;
  padding: 0 50px;
}

.grid {
  display: flex;
  flex-wrap: wrap;
}

.item {
  border: 1px solid black;
  margin: 1px;
}
<div class="page">
  <div class="grid">
    <div class="item">Foofoofoo</div>
    <div class="item">Bar</div>
    <div class="item">BazBaz</div>
    <div class="item">QuuxQuuxQuux</div>
  </div>
</div>

为什么?

因为在第二个代码和@Michael Benjamin 代码中,.grid 在技术上是centered视觉

看到整个 div 居中。问题是.grid div 不会根据里面的内容改变它的宽度。但是,根据其父div.page)的宽度。

我知道它不能解决您的问题,但我只是想确保您现在了解主要问题。所以,也许你可以通过其他方式找到解决方案。

【讨论】:

    【解决方案3】:

    对于这种情况,我认为唯一的解决方案是使用 JavaScript。

    在这段代码中,我们得到每个.item 的宽度。然后我们设置.flex宽度=总.item宽度。如果总.item 宽度小于.page 宽度-我们设置.flex 宽度=((总.item 宽度)-(最后.item 宽度))。我希望 JS 的可读性很好。如果您需要更多解释 - 可以在 cmets 中给出。

    请注意,这不是灵活且通用的解决方案,但在这种特殊情况下效果很好,因为它是为它编写的。

    这个 sn-p 你只能测试浏览器窗口大小的变化。最好使用带有设计工具栏模式的 Snippet Full page 和 Chrome 控制台签入。或者这里https://jsfiddle.net/focusstyle/sh6dnLvt/1/

    window.addEventListener("resize", function() {
      flexWidth();  
    });
    
    function flexWidth() {
      let page = document.querySelector('.page');
      let flex = document.querySelector('.flex');
      let totalWidth = biggesWidth = itemWidth = lastWidth = 0;
      let flexItem = document.querySelectorAll('.item');
      for (let i = 0; i < flexItem.length; i += 1) {
        itemWidth = flexItem[i].offsetWidth;
        if (biggesWidth<itemWidth) {
          biggesWidth = itemWidth;
        } 
        biggesWidth = flexItem[i].offsetWidth;
        totalWidth += itemWidth;
        lastWidth = itemWidth; 
      }
      if (totalWidth > page.clientWidth) {
        totalWidth = totalWidth - lastWidth;
      }
      totalWidth += 1;  
      flex.style.cssText = "min-width: "+biggesWidth+"px; max-width: "+totalWidth+"px;"; 
    }
    .page {
      border: 1px solid black;
      overflow: hidden;
      text-align: center;
    }
    
    .flex {
      display: inline-flex;
      flex-wrap: wrap;
      justify-content: flex-start;
    }
    
    .item {
      border: 1px solid black;
    }
    <div class="page">
      <div class="flex">
        <div class="item">
          Foofoofoo
        </div>
        <div class="item">
          Bar
        </div>
        <div class="item">
          BazBaz
        </div>
        <div class="item">
          QuuxQuuxQuux
        </div>
      </div>
    </div>

    【讨论】:

      【解决方案4】:

      CSS 网格方法-
      根本答案 - joe82

      .container {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(179px, max-content));
        grid-gap: 10px;
        justify-content: center;
        background: #999;
        overflow: hidden;
        padding: 10px;
      }
      
      .background {
        width: 179px;
        height: 64px;
        background: #99d9ea;
      }
      
      .background .child {
        border: 2px solid #000;
      }
      
      .background:nth-child(1) .child {
        width: 110px;
        height: 50px;
      }
      
      .background:nth-child(2) .child {
        width: 120px;
        height: 60px;
      }
      
      .background:nth-child(3) .child {
        width: 50px;
        height: 55px;
      }
      
      .background:nth-child(4) .child {
        width: 175px;
        height: 40px;
      }
      <div class="container">
          <div class="background"><div class="child"></div></div>
          <div class="background"><div class="child"></div></div>
          <div class="background"><div class="child"></div></div>
          <div class="background"><div class="child"></div></div>
      </div>

      这里是新手!

      我承认上面的输出不是你想要的,但这是我最好的尝试,使用 CSS 网格。在这里,您可以理解,如果我们想让它具有响应性,那么需要一个最小宽度,在此之后,列(内容)将被带到下一行,并且该宽度在此处定义(grid-template-columns: repeat(auto-fit, minmax(204px, max-content));)204px。但正因为如此,每列至少会占用这么多的宽度,这就是为什么我用蓝色背景表示一列的实际尺寸和边框内的实际内容。我只是将它发布为您的确认和方法,以便您可以更接近到实际答案。

      顺便说一下,Flex Approach-
      根本思想 - 随机 COSMOS

      .container {
        min-width: 130px;
        max-width: 340px;
        overflow: auto;
        background: #999;
        padding: 10px 40px;
        resize: horizontal;
        border: 1px solid #000;
      }
      
      .main-content {
        display: flex;
        flex-wrap: wrap;
        background: #fff;
        max-width: 340px;
        overflow: hidden;
      }
      
      .child {
        margin: 5px;
        padding: 5px;
        background: #99d9ea;
        border: 1px solid black;
      }
      <div class="container">
        <div class="main-content">
          <div class="child">Foofoofoo</div>
          <div class="child">Bar</div>
          <div class="child">BazBaz</div>
          <div class="child">QuuxQuuxQuux</div>
        </div>
      </div>
      Resize the window or the above div to see the results 

      上面告诉div居中,内容在左边,但不根据内容调整大小。

      我的个人意见 -

      你应该使用@media 让它响应,就像你想要的那样,这就像为一个简单的输出编写很多代码,但它可以为你的辛勤工作带来最好和令人满意的结果和时间!

      如果你想让我让它响应你,请告诉我,我的意思是就像一个演示-

      请注意,
      奥姆乔杜里

      【讨论】:

        【解决方案5】:

        我不知道我是否遗漏了什么,但我认为这可以通过 flexbox 实现。

        你必须得到这个结构:

        <div class="container">
          <div class="row">
            <div class="item item-1"></div>
            <div class="item item-2"></div>
            <div class="item item-3"></div>
            <div class="item item-4"></div>
          </div>
        </div>
        

        row 有一个max sizemargin: 0 auto(要居中)

        Here is my example on Codepen

        【讨论】:

        • 仅考虑您的屏幕截图,而不是 OP 想要的输出。蓝色背景应该等于换行后的内容(两边的空闲空间为0)
        • 这与蓝色背景无关。这是关于内容的中心化。红色容器左边缘到左孩子的距离应该与红色容器右边缘到右孩子的距离相同。
        【解决方案6】:

        让我们先了解一下 210px。当您编写以下代码时:

        grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
        

        浏览器知道何时将项目包裹起来。它确保您的网格项目始终宽于 210 像素或至少等于 210 像素。

        如果浏览器有 420px 宽度可用,它会将 2 个项目放在一行中。 如果浏览器有 630px 宽度可用,它将把 3 个项目放在一行中,以此类推...

        You can learn about CSS grids here

        如果您仍然不希望最小内容为 210 像素,您可以随时在 CSS 中编写媒体查询。

        可能适合您要求的另一件事是为您的网格项目提供最小宽度和最大宽度。

        希望它能拯救你的一些头发。

        【讨论】:

        • 嗯,我确实理解了那部分。我不明白为什么规范需要固定宽度。换句话说,为什么它不能根据单个项目的宽度同时应用自动最小宽度和自动最大宽度?
        猜你喜欢
        • 2019-04-07
        • 1970-01-01
        • 2018-12-14
        • 2019-07-19
        • 2019-11-26
        • 1970-01-01
        • 1970-01-01
        • 2020-04-10
        • 2023-02-23
        相关资源
        最近更新 更多