使用 CSS `grid-template-rows: subgrid` 和跨行的项目,如何获得新的 'multi-row' 行?

With CSS `grid-template-rows: subgrid`, and items spanning rows, how do I get a new 'multi-row' row?

我正在使用 Firefox——迄今为止唯一支持 subgrid 的浏览器——并且实际上正在尝试使用 subgrid 功能:

const D = document,
  gen = function*() {
    let start = 125;
    while (true) {
      yield ++start;
    }
  },
  imgNum = gen(),
  create = (tag) => document.createElement(tag),
  addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
      articleCount = main.querySelectorAll('article').length,
      h2 = create('h2'),
      img = create('img'),
      p = create('p');
    h2.textContent = `Title ${articleCount + 1}`;
    img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
    p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
    article.append(h2, img, p);
    main.querySelector('section').append(article);
  }

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-auto-columns: 1fr;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 2em min-content fit-content;
  grid-template-areas: 'header' 'image' 'description';
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: auto / span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  grid-area: header;
  font-weight: 600;
  text-align: center;
}

article img {
  grid-area: image;
  object-fit: cover;
  width: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {
  grid-area: description;
}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

JS Fiddle demo.

有了这个,我单击 <button> 元素并创建了一个新的 <article> 元素,给出以下 HTML(对于第一个创建的 <article>):

<article>
  <h2>Title 4</h2>
  <img src="https://picsum.photos/id/125/200/">
  <p>Lorem ipsum dolor sit amet <!-- removed for brevity --></p>
</article>

并且 <article> 已正确附加到 <section>。到目前为止,还不错。

但是,这些文章的样式为:

article {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: auto / span 3;
}

并且需要使用 grid-row: auto / span 3 才能使 <article> 跨越所需的三行,从而使同一行上相邻 <article> 元素的后代的元素大小保持一致。

但是,使用 <button> 添加另一个 <article> 会导致某种形式的视觉混乱,其中 <h2> 位于 <img> 元素后面,并且<img> 垂直 – 在页面上 – 低于 <p>:

(这在上面的代码段中得到了准确的复制,但显然需要 Firefox 71 或更高版本。)

至于我的期望:我希望放置在隐式行上的元素能够准确反映第一行的布局,以及显式行。

我第一次尝试实现这一点在于添加:

section {
  /* ... */
  grid-auto-rows: repeat(1, 2em min-content fit-content);
}

这显然行不通。

const D = document,
  gen = function*() {
    let start = 125;
    while (true) {
      yield start++;
    }
  },
  imgNum = gen(),
  create = (tag) => document.createElement(tag),
  addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
      articleCount = main.querySelectorAll('article').length,
      h2 = create('h2'),
      img = create('img'),
      p = create('p');
    h2.textContent = `Title ${articleCount + 1}`;
    img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
    p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
    article.append(h2, img, p);
    main.querySelector('section').append(article);
  }

document.querySelector('button').addEventListener('click', addCard);
*,
 ::before,
 ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-auto-columns: 1fr;
  grid-auto-rows: repeat(1, 2em min-content fit-content);
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 2em min-content fit-content;
  grid-template-areas: 'header' 'image' 'description';
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: auto / span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  grid-area: header;
  font-weight: 600;
  text-align: center;
}

article img {
  grid-area: image;
  object-fit: cover;
  width: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {
  grid-area: description;
}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

JS Fiddle demo.

我显然遗漏了一些东西 – 但我相信这个用例已经预料到了 – 非常感谢帮助。

问题是您定义了 一个 模板,这意味着只有一行将遵循该模板,而所有其他行都将有一个隐式模板。您需要的是一个重复的模板,为此您需要去掉命名区域。

基本上,您不需要做任何事情,因为浏览器会为您完成这项工作。使文章跨越 3 行就足够了。请注意,您的 grid-template-rows (2em min-content fit-content) 无效

const D = document,
    gen = function* (){
    let start = 125;
    while (true){
        yield start++;
    }
  },
  imgNum = gen(),
    create = (tag) => document.createElement(tag),
    addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
        articleCount = main.querySelectorAll('article').length,
        h2 = create('h2'),
      img = create('img'),
      p = create('p');
  h2.textContent = `Title ${articleCount + 1}`;
  img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
  p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
  article.append(h2, img, p);
  main.querySelector('section').append(article);
}

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-template-columns: repeat(3, 1fr);
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  font-weight: 600;
  text-align: center;
  height: 2em;
}

article img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

如果您需要定义行的大小,则必须使用 grid-auto-rows: 1st 2nd 3rd; 而不是 grid-template-rows: 1st 2nd 3rd;

const D = document,
    gen = function* (){
    let start = 125;
    while (true){
        yield start++;
    }
  },
  imgNum = gen(),
    create = (tag) => document.createElement(tag),
    addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
        articleCount = main.querySelectorAll('article').length,
        h2 = create('h2'),
      img = create('img'),
      p = create('p');
  h2.textContent = `Title ${articleCount + 1}`;
  img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
  p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
  article.append(h2, img, p);
  main.querySelector('section').append(article);
}

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 2em 100px auto;
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  font-weight: 600;
  text-align: center;
}

article img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

应该注意的是,结果在不支持 subgrid 的浏览器上看起来也不错,因为默认情况下所有元素都会正确放置在 article 中。你只会错过间隙和对齐。

空缺可以加到article如果需要:

const D = document,
    gen = function* (){
    let start = 125;
    while (true){
        yield start++;
    }
  },
  imgNum = gen(),
    create = (tag) => document.createElement(tag),
    addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
        articleCount = main.querySelectorAll('article').length,
        h2 = create('h2'),
      img = create('img'),
      p = create('p');
  h2.textContent = `Title ${articleCount + 1}`;
  img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
  p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
  article.append(h2, img, p);
  main.querySelector('section').append(article);
}

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 2em 100px auto;
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  padding: 0.5em;
  gap: inherit;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  font-weight: 600;
  text-align: center;
}

article img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>