在 CSS 网格中指定哪些单元格扩展单元格区域而哪些不扩展单元格的内容

Specifying content of which cells expand cell areas and which do not in CSS Grid

我希望在网格中有一个包含不重要信息的单元格,它不会使任何其他单元格扩展到超出绝对需要的范围。它的高度不能超过其他单元格的高度,宽度应该是空白区域的宽度。

假设每个项目我都有一个缩略图、一个标题(可展开的单行)、信息(零行或多行)、描述(可能很长,只有在空 space 时才会显示)。

我想到了这个:

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}
.item {
  display: grid;
  grid-template-areas:
    "thumb thumb thumb thumb"
    "sl    title desc  sr   "
    "sl    info  desc  sr   ";
  grid-template-columns:
    auto auto 1fr auto;
  grid-template-rows:
    auto auto 1fr;
  min-width: calc(var(--min-item-size) + 4px);
  max-width: calc(var(--max-item-size) + 4px);
  border: dotted 2px darkblue;
  margin: 4px 2px auto 2px;
}
.item > * {
  min-width: 0;
  min-height: 0;
  border: dotted 1px dodgerblue;
  margin: 1px;
}
.thumb {
  grid-area: thumb;
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}
.title {
  grid-area: title;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.info {
  grid-area: info;
  overflow: hidden;
  text-overflow: ellipsis;
}
.desc {
  grid-area: desc;
  position: relative;
  overflow: hidden;
}
.desc-text::after {
  position: absolute; /*clean hack*/
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  content: var(--lorem-ipsum);
}
.desc-text.short::after {
  content: var(--lorem-ipsum-short);
}
:root {
  --lorem-ipsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
  --lorem-ipsum-short: "Lorem";
  --min-item-size: 100px;
  --max-item-size: 150px;
}
<div class="list">
  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item" style="grid-template-columns: 22px auto 1fr 22px /*dirty hack*/">
    <div class="thumb" style="width: 150px">5</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
</div>

我不得不将描述放入相对定位单元格内的绝对定位容器中。这使得网格忽略内容并且有点工作,但是有几个问题:

  1. 如果描述很短(见项目#5),我不能将信息块居中,因为1fr用于描述,所以设置间距列(slsr) 除了 0 之外的任何东西都不起作用(样式属性形式的脏 hack 不算数,它只是为了显示间距单元格的使用)。

  2. 它看起来不像是纯粹的 CSS 网格解决方案。我想知道是否有更多 "native" 方法来指定细胞生长如何受内容影响。


如果您想要 "clean start",您可以删除 CSS 中的 position: absolute; /*clean hack*/ 行和 HTML 中的 style="grid-template-columns: 22px auto 1fr 22px /*dirty hack*/" 属性。一些定位 CSS 属性已过时,但它们不再影响定位。如果您愿意,也可以删除它们。

干净的开始:

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}
.item {
  display: grid;
  grid-template-areas:
    "thumb thumb thumb thumb"
    "sl    title desc  sr   "
    "sl    info  desc  sr   ";
  grid-template-columns:
    auto auto 1fr auto;
  grid-template-rows:
    auto auto 1fr;
  min-width: calc(var(--min-item-size) + 4px);
  max-width: calc(var(--max-item-size) + 4px);
  border: dotted 2px darkblue;
  margin: 4px 2px auto 2px;
}
.item > * {
  min-width: 0;
  min-height: 0;
  border: dotted 1px dodgerblue;
  margin: 1px;
}
.thumb {
  grid-area: thumb;
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}
.title {
  grid-area: title;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.info {
  grid-area: info;
  overflow: hidden;
  text-overflow: ellipsis;
}
.desc {
  grid-area: desc;
  overflow: hidden;
}
.desc-text::after {
  content: var(--lorem-ipsum);
}
.desc-text.short::after {
  content: var(--lorem-ipsum-short);
}
:root {
  --lorem-ipsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
  --lorem-ipsum-short: "Lorem";
  --min-item-size: 100px;
  --max-item-size: 150px;
}
<div class="list">
  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">5</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
</div>

题目有点难懂,举的例子比较绕。如果我没有直接回答你的问题,我深表歉意。

我认为您要问的(根据您的示例及其要求)是:"Can you prioritise which elements grow or shrink regardless of their content?"

这个问题的答案是否定的,但这并不意味着您仍然只使用 CSS 就无法实现您想要的。请参阅下面的代码片段,了解我是如何处理/解决问题的。

请注意:我认为您尝试使用网格规则或什至因为我 运行 遇到与您尝试解决此问题时遇到的问题相同的问题而使事情变得困难弹性。

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}

.item {
  min-width: 100px;
  max-width: 150px;
  border: solid 1px black;
  float: left;
  margin: 10px;
}

.thumb {
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}

.center {
  float: left;
  left: 50%;
  transform: translateX(-50%);
  position: relative;
  max-width: 150px;
}

.meta-wrap {
  float: left;
  max-width: 100%;
}

.desc-wrap {
  overflow: hidden;
  /*
  Make sure this doesn't grow out the rest of the item.
  This can be a static height because if the info is long enough to grow the height
  then the description wouldn't be seen anyway.
  */
  max-height: 27px;
}

.title,
.info,
.text {
  overflow: hidden;
  text-overflow: ellipsis;
}

.title {
  background: orange;
  white-space: nowrap;
}

.info {
  background: tomato;
}

.text {
  background: aqua;
}
<div class="list">

  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Short</div>
        <div class="info">Short</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem ipsum text and stuff goes here, cool!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Long title goes here that should cut off</div>
        <div class="info">long description goes here</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem ipsum text and stuff goes here, cool!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Short</div>
        <div class="info">Short</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Long title goes here that should cut off</div>
        <div class="info">long description goes here</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem ipsum text and stuff goes here, cool!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 150px">5</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Short</div>
        <div class="info">Short</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Long title goes here that should cut off</div>
        <div class="info">long description goes here</div>
      </div>
      <div class="desc-wrap">
        <div class="text"></div>
      </div>
    </div>
  </div>

</div>

我认为我已经找到了一个很好的解决方案。

唯一的缺点是我需要在.title.info上设置max-width

你的第一个问题是水平分布space。您希望第二列将尽可能多的 space 提供给 title。但不超过所包含元素的自然宽度。所以,我们需要一些与内容相关的东西,以及它的最大宽度,max-content.

然后我们想要第三列,desc,如果有左边的宽度,也不要超过它可以填充的宽度。这可以通过 min-max(0, max-content)

来实现

最后,我们希望任何剩下的 space 进入第 1 和第 4 列,1fr

这种安排之所以有效,是因为免费的 space 分两轮分配,一轮用于 minmax 函数,第二轮用于 fr 元素。

此设置的问题是 max-content of title 会溢出容器。我们不能(我认为)在网格本身上解决这个问题,我们需要在元素本身上设置一个最大宽度。

起初,您可能认为这里的 minmax 函数会有帮助。不,它不能。可以看到一个解释here

另一个问题是处理 desc 的高度。由于它跨越 2 行,但两者都必须对内容敏感,我们在这里运气不好。

幸运的是,您在这里已经有了一个嵌套结构。使容器 overflow: hidden 并限制内部元素的高度就可以了。内部元素的内容没有足够的高度,但由于 溢出:none,它们得以显示。并且容器将高度适配到网格,并裁剪掉溢出的内容

:root {
  --lorem-ipsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
  --lorem-ipsum-short: "Lorem";
  --min-item-size: 100px;
  --max-item-size: 150px;
  --max-item-desc-height: 32px; /* added */
}

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}
.item {
  display: grid;
  grid-template-areas:
    "thumb thumb thumb thumb"
    "sl    title desc  sr   "
    "sl    info  desc  sr   ";
  grid-template-columns: 1fr max-content minmax(0px, max-content) 1fr; /* changed */
  grid-template-rows: auto auto 1fr;
  min-width: calc(var(--min-item-size) + 4px);
  max-width: calc(var(--max-item-size) + 4px);
  border: dotted 2px darkblue;
  margin: 4px 2px auto 2px;
}
.item > * {
  min-width: 0;
  min-height: 0;
  border: dotted 1px dodgerblue;
  margin: 1px;
}
.thumb {
  grid-area: thumb;
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}
.title {
  grid-area: title;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: var(--max-item-size); /* added */
}
.info {
  grid-area: info;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: var(--max-item-size); /* added */
}
.desc {
  grid-area: desc;
  overflow: hidden;
  max-height: var(--max-item-desc-height); /* added */
}
.desc-text::after {
  content: var(--lorem-ipsum);
}
.desc-text.short::after {
  content: var(--lorem-ipsum-short);
}
<div class="list">
  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">5</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
</div>