具有 CSS 网格布局的网格项内元素的高度相等

2022-01-18 00:00:00 grid rows css height css-grid

我在 4+ 长度的 div 中有一系列文章,没有任何舍入行标记.我需要将它表示为每行 3 篇文章(列)的表格,可能使用 display:grid.每篇文章都有一个页眉、一个节和一个页脚.

如何在每行文章内实现每个标题的高度相等、每个部分的高度相等以及与文章底部对齐的页脚高度相等?甚至可能吗?我应该使用 display: table 吗?

PS 我需要根据屏幕宽度动态更改每行的文章数.谢谢.

HTML:

body {宽度:100%;最大宽度:1024px;边距:自动;}.容器 {显示:网格;网格模板列:重复(3、1fr);}.container 文章 {显示:网格;}文章标题{背景颜色:#eeeeee;}文章部分{背景颜色:#cccccc;}文章页脚{背景颜色:#dddddd;}

<div class="container"><文章><标题><h2>标题</h2><h2>标题</h2></标题><部分><p>内容</p></部分><页脚><p>页脚</p></页脚></文章><文章><标题><h2>标题</h2></标题><部分><p>内容</p><p>内容</p><p>内容</p><p>内容</p><p>内容</p></部分><页脚><p>页脚</p><p>页脚</p></页脚></文章><文章><标题><h2>标题</h2></标题><部分><p>内容</p><p>内容</p><p>内容</p></部分><页脚><p>页脚</p></页脚></文章><文章><标题><h2>标题</h2></标题><部分><p>内容</p><p>内容</p><p>内容</p><p>内容</p></部分><页脚><p>页脚</p><p>页脚</p></页脚></文章></div>

注意:JS 已被弃用.

grid-auto-rows: 1fr 解决方案导致:

解决方案

有可能吗?

tldr;是的.

Codepen 演示 #1

Codepen Demo #2(使用 SASS,可配置)<小时>这里的困难在于每篇文章本身都是一个网格,因此任何一篇文章都不了解另一篇文章.正因为如此,一篇文章的组件(如标题)无法根据另一篇文章中标题的高度进行调整.

实际上有一种方法可以使用 css 网格 &无需更改任何标记!

我们可以使用 CSS 将结构扁平化",这样所有文章的所有组件都只是一个 CSS 网格(文章容器)的一部分.

通过设置文章 display: contents

显示:内容 (caniuse)

来自 Caniuse:

<块引用>

display: contents 使元素的子元素看起来好像它们是元素父元素的直接子元素,忽略元素本身.当应忽略包装器元素时,这可能很有用使用 CSS 网格或类似布局技术时.

所以如果我们用 display: contents

设置文章

.container 文章 {显示:内容;}

现在所有的页眉、节和页脚都变成了(直接的)网格项目(容器的 - 它有 display:grid),我们可以使用 grid-template-areas 来安排它们 属性.

.container {显示:网格;网格列间隙:1em;/* 文章之间的水平间距 */网格模板列:重复(3、1fr);网格模板区域:header1 header2 header3"第 1 节第 2 节第 3 节"页脚 1 页脚 2 页脚 3"标题 4 标题 5 标题 6"第 4 节第 5 节第 6 节"页脚 4 页脚 5 页脚 6"}

由于每个页眉/节/页脚只占用一个单元格 - 这迫使它们占用相同的垂直高度.所以例如header1、header2 和 header3 都将具有相同的高度,而不管它们的内容如何.

现在在每个单元格上设置 grid-area 属性.

article:nth-child(1) header {网格区域:header1;}文章:第n个孩子(2)标题{网格区域:header2;}文章:第n个孩子(3)标题{网格区域:header3;}文章:第n个孩子(4)标题{网格区域:header4;}文章:第n个孩子(1)部分{网格区域:section1;}...文章:第n个孩子(4)部分{网格区域:section4;}文章:第n个孩子(1)页脚{网格区域:footer1;}...文章:第n个孩子(4)页脚{网格区域:footer4;}

最后设置每行文章之间的垂直间距(从第二行文章开始):

article:nth-child(n + 4) header {上边距:1em;}

演示:

body {宽度:100%;最大宽度:1024px;边距:自动;}.容器 {显示:网格;网格列间隙:1em;网格模板列:重复(3、1fr);网格模板区域:header1 header2 header3"第 1 节第 2 节第 3 节"页脚 1 页脚 2 页脚 3"标题 4 标题 5 标题 6"第 4 节第 5 节第 6 节"页脚 4 页脚 5 页脚 6"}.container 文章 {显示:内容;}文章标题{背景颜色:#eeeeee;}文章部分{背景颜色:#cccccc;}文章页脚{背景颜色:#dddddd;}文章:第n个孩子(n + 4)标题{上边距:1em;}文章:第n个孩子(1)标题{网格区域:header1;}文章:第n个孩子(2)标题{网格区域:header2;}文章:第n个孩子(3)标题{网格区域:header3;}文章:第n个孩子(4)标题{网格区域:header4;}文章:第n个孩子(1)部分{网格区域:section1;}文章:第n个孩子(2)部分{网格区域:section2;}文章:第n个孩子(3)部分{网格区域:section3;}文章:第n个孩子(4)部分{网格区域:section4;}文章:第n个孩子(1)页脚{网格区域:footer1;}文章:第n个孩子(2)页脚{网格区域:footer2;}文章:第n个孩子(3)页脚{网格区域:footer3;}文章:第n个孩子(4)页脚{网格区域:footer4;}

<div class="container"><文章><标题><h2>标题</h2><h2>标题</h2></标题><部分><p>内容</p></部分><页脚><p>页脚</p></页脚></文章><文章><标题><h2>标题</h2></标题><部分><p>内容</p><p>内容</p><p>内容</p><p>内容</p><p>内容</p></部分><页脚><p>页脚</p><p>页脚</p></页脚></文章><文章><标题><h2>标题</h2></标题><部分><p>内容</p><p>内容</p><p>内容</p></部分><页脚><p>页脚</p></页脚></文章><文章><标题><h2>标题</h2></标题><部分><p>内容</p><p>内容</p><p>内容</p><p>内容</p></部分><页脚><p>页脚</p><p>页脚</p></页脚></文章></div>

Codepen 演示)

当然,除了使用 grid-template-areas + grid-area 属性,我们还可以使用 grid-row +grid-column 属性来实现相同的结果 - Codepen 演示

<小时>

注意:我确实意识到上述内容很冗长,并不是最佳解决方案 - 但我的意思是这是可能的.此外,我们可以使用 SASS 循环使代码更简洁且可配置.

如果有某种方法可以使用 grid-template-areas 来重复一个模式 - 比如:

伪代码(不合法):

网格模板区域:重复(header1 header2 header3"第 1 节第 2 节第 3 节""页脚 1 页脚 2 页脚 3")

...然后我们可以通过使用 nth-child 设置网格区域来获得一个更动态的解决方案,该解决方案适用于 n 文章解决方案:

article:nth-child(3n + 1) header {网格区域:header1;}

等等....但我认为目前不可能(或者可能没有必要,因为 subgrids 能做到这一点吗?)

<小时>

注意:

Grid Layout Module Level 2 引入了 Subgrids,这将使这个问题变得更容易解决而不必使用 display: contents

<小时><块引用>

我应该使用 display: table 吗?

对于您需要的布局 - display:table 不会有太大帮助.首先,您必须完全重组您的标记,将 文章组件 与文章相对应地组合在一起,您必须修改表格的样式以使其看起来像文章",但即便如此 - 表格不要包装,因此您需要将每三篇文章包装到一个单独的表格中......即使它可能会非常混乱且难以维护.

I have a sequence of articles inside a div of 4+ length, without any rounding row tag. I need to represent it as a table of 3 articles (columns) per row, probably with display: grid. Every article has a header, a section and a footer.

How would I implement an equal height for each header, an equal height for each section, and an equal height footer, aligned to the bottom of the article, inside each row of articles? Is it even possible? Should I use display: table?

PS I need to change the number of articles per row dynamically, depending on screen width. Thanx.

HTML:

body {
  width: 100%;
  max-width: 1024px;
  margin: auto;
}

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.container article {
  display: grid;
}

article header {
  background-color: #eeeeee;
}

article section {
  background-color: #cccccc;
}

article footer {
  background-color: #dddddd;
}

<div class="container">
  <article>
    <header>
      <h2>Header</h2>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
    </footer>
  </article>
  <article>
    <header>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
      <p>Footer</p>
    </footer>
  </article>
  <article>
    <header>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
    </footer>
  </article>
  <article>
    <header>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
      <p>Footer</p>
    </footer>
  </article>
</div>

NB: JS is deprecated.

https://codepen.io/yudnikov/pen/mBvbGW?editors=1100#0

This solution:

grid-auto-rows: 1fr; 

has been proposed as a duplicate, which it is not. It will only give the articles equal height, while e.g. the headers of each article remains differently sized.

I originally had this problem:

And the grid-auto-rows: 1fr solution results in this:

解决方案

Is it even possible?

tldr; Yes.

Codepen Demo #1

Codepen Demo # 2 (Uses SASS and is configurable)


The difficulty here is that each article is a grid within itself and therefore any one article has no knowledge about another. Because of this, there is no way for a component of one article like a header to adjust according to the height of a header in another article.

There is actually a way to pull this off with css grids & without changing any markup!

We could 'flatten' the structure with CSS such that all the components of all the articles are part of just one CSS grid - the article container.

This is possible without even changing the current markup by setting the articles with display: contents

display: contents (caniuse)

From Caniuse:

display: contents causes an element's children to appear as if they were direct children of the element's parent, ignoring the element itself. This can be useful when a wrapper element should be ignored when using CSS grid or similar layout techniques.

So if we set the articles with display: contents

.container article {
  display: contents;
}

Now all of the headers, sections and footer become (direct) grid items (of the container - which has display:grid) which we can arrange using the grid-template-areas property.

.container {
  display: grid;
  grid-column-gap: 1em; /* horizontal gap between articles */
  grid-template-columns: repeat(3, 1fr);

  grid-template-areas: "header1 header2 header3" 
                       "section1 section2 section3"
                       "footer1 footer2 footer3"
                       "header4 header5 header6" 
                       "section4 section5 section6"
                       "footer4 footer5 footer6"
}

Since each header/section/footer take up exactly one cell - this forces them to take up the same vertical height. So e.g. header1,header2 and header3 will all have the same height regardless of their content.

Now set the grid-area properties on each of the cells.

article:nth-child(1) header {
  grid-area: header1;
}
article:nth-child(2) header {
  grid-area: header2;
}
article:nth-child(3) header {
  grid-area: header3;
}
article:nth-child(4) header {
  grid-area: header4;
}
article:nth-child(1) section {
  grid-area: section1;
}
...
article:nth-child(4) section {
  grid-area: section4;
}
article:nth-child(1) footer {
  grid-area: footer1;
}
...
article:nth-child(4) footer {
  grid-area: footer4;
}

Finally, set a vertical gap between each row of articles (starting from the second row of articles):

article:nth-child(n + 4) header {
  margin-top: 1em;
}

Demo:

body {
  width: 100%;
  max-width: 1024px;
  margin: auto;
}

.container {
  display: grid;
  grid-column-gap: 1em;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas: "header1 header2 header3" 
                      "section1 section2 section3"
                        "footer1 footer2 footer3"
                        "header4 header5 header6" 
                      "section4 section5 section6"
                        "footer4 footer5 footer6"
}

.container article {
  display: contents;
}

article header {
  background-color: #eeeeee;
}

article section {
  background-color: #cccccc;
}

article footer {
  background-color: #dddddd;
}

article:nth-child(n + 4) header {
  margin-top: 1em;
}

article:nth-child(1) header {
  grid-area: header1;
}
article:nth-child(2) header {
  grid-area: header2;
}
article:nth-child(3) header {
  grid-area: header3;
}
article:nth-child(4) header {
  grid-area: header4;
}
article:nth-child(1) section {
  grid-area: section1;
}
article:nth-child(2) section {
  grid-area: section2;
}
article:nth-child(3) section {
  grid-area: section3;
}
article:nth-child(4) section {
  grid-area: section4;
}
article:nth-child(1) footer {
  grid-area: footer1;
}
article:nth-child(2) footer {
  grid-area: footer2;
}
article:nth-child(3) footer {
  grid-area: footer3;
}
article:nth-child(4) footer {
  grid-area: footer4;
}

<div class="container">
    <article>
        <header>
            <h2>Header</h2>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
        </footer>
    </article>
    <article>
        <header>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
            <p>Footer</p>
        </footer>
    </article>
    <article>
        <header>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
        </footer>
    </article>
    <article>
        <header>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
            <p>Footer</p>
        </footer>
    </article>
</div>

Codepen Demo)

Of course, instead of using the grid-template-areas + grid-area properties we could instead use the grid-row + grid-column properties to achieve the same result - Codepen demo


Note: I do realise that the above is verbose and not exactly an optimal solution - but my point here is that it is possible. Also, we could use SASS loops to make that code much cleaner and also configurable.

It might be nice if there was some way to repeat a pattern using grid-template-areas - something like:

pseudo-code (not legal):

grid-template-areas: repeat("header1 header2 header3" 
                           "section1 section2 section3"
                           "footer1 footer2 footer3")

...then we could get a more dynamic solution which would work for n articles solution by setting the grid-area using nth-child like:

article:nth-child(3n + 1) header {
  grid-area: header1;
} 

etc. ... but I don't think that's possible at the moment (or maybe it's not necessary because subgrids will be able to do this?)


NB:

Grid Layout Module Level 2 introduces Subgrids which will make this problem easier to solve and without having to use display: contents


Should I use display: table?

For the layout which you need - display:table won't help much. Firstly, you'd have to totally restructure your markup to group article components together as apposed to articles, you'd have to hack around to style the table to look like 'articles' but even then - tables don't wrap so you'd need to wrap every three articles into a separate table.... even if it were possible it would be really messy and hard to maintain.

相关文章