为什么是display:contents而不是CSS Grid的subgrid

发布于 大漠

在CSS Grid Layout中,如果没有人提出display:contents;来解决一部分问题,那么你就无法深入的讨论CSS Grid Layout中的subgrid,那么我们真的需要subgrid?事实并非如此,display:contents的确帮我们解决了这类问题,但这些问题与subgrid帮助我们解决的问题不同。在这篇文章中,我将用实例来介绍他们的不同之处。

如果你觉得这些特性对您来说是很新的东西,那么之前我写过一些相关的文章,比如在Firefox已实现以及Chrome正在实现的display:contents;。我还写了有关于CSS Grid Layout Level1规范中的subgrid特性。下面这些帖子将为这篇文章提供一些背景,如果你阅读过下面的这些文章,将能更好的帮助你理解这篇文章所要阐述的观点:

我的布局示例

假设内容从CMS中完美生成出来,只去做布局,这是多么幸福的事情。CMS生成出来的内容甚至连内容长度都是一样的。

乍一看,这是看起来是一个非常简单的网格布局,我们只需要几行代码就可以实现:

.grid {
    display: grid;
    max-width: 960px;
    margin: 0 auto;
    grid-template-columns: repeat(3, 1fr);
    grid-gap: 20px;
}

然而,在真实的世界中,内容并不会像CMS自动生成的一样,那么我们的布局就会变成这样:

每个卡片内的项目并不是网格容器的子元素,这样一来我们就没有办法将内部元素彼此对齐。

我能做的最好的事情就是让每一张卡片都变成是一个网格容器,或者是一个Flex项目,然后把它们推到底部。这有点不好,但我想要的是,卡片内部的内容也是每个行。

CSS Grid Layout Level1规范中删除的subgrid特性能帮我们实现这一点。我无法向你能工作的代码,但我可以解释它是如何与我们的设计一起工作的。

每个卡片都有四个子元素,每个子元素对应一行:titleimagecontentfooter

.card元素是网格容器的子元素,因此它们可以直接使用网格布局,这意味着我们的卡片显示在行和列中。在.card元素中我们需要四个行,所以设置.card跨网格中的四行。然后,我们需要给.card本身设置为display:subgrid;,这意味着我们的.card的四个子元素也将参与到父网格的布局中,并为把每一个元素放置在行中。这个时候每个卡生都将做相同的事情,整个网格的行也会对齐。

.card {
    border: 4px solid rgb(24,154,153);
    background-color: #fff;
    grid-row: auto / span 4;
    display: subgrid;
}

这并没有真正的实现。我的示例代码还是基于CSS Grid Layout Level1,但这个在Level1规范中已经被删除了,但我也提了一些简化的建议。在现实生活中我们需要围绕卡片内部的空白间距做更多的工作,而并不是内部的行做操作,然后而内部的行让我对齐内容变得更容易得多。

display:contents可以做相同的事情?

display:contents可以删除元素的盒模型规则。如果你希望一个元素要语义化而不是视觉目的,并且希望它能够参与网格或Flex布局的子元素,那么这一点非常有用。

如下面的示例,有两组HTML结构。它们是相同的,除了一个是.flex,另一个是.grid

<div class="flex">
    <div>One</div>
    <div>Two</div>
    <div class="nested">
        <div>Nested One</div>
        <div>Nested Two</div>
    </div>
</div>

<div class="grid">
    <div>One</div>
    <div>Two</div>
    <div class="nested">
        <div>Nested One</div>
        <div>Nested Two</div>
    </div>
</div>

我们可以做两个非常简单的布局,一个是Flex布局,一个是Grid布局。我将在容器和其子元素上添加不同的边框颜色:

.flex {
    display: flex;
    border: 8px solid rgb(3,99,143);
}

.flex > * {
    flex: 1;
    border: 8px solid rgb(24,154,153);
}

.grid {
    display: grid;
    border: 8px solid rgb(3,99,143);
    grid-template-columns: 
        repeat(4,minmax(200px, 1fr));
    grid-gap: 8px;
}

.grid > * {
    border: 8px solid rgb(24,154,153);
}

你可以看到,只有容器的子元素才成为Grid或Flex布局的一部分。嵌套元素依旧是块显示。

嵌套元素的项有一个带.nested容器。我们可以针对它做一些事情,如果我们在.nested上使用display:contents;,那么.nested容器的元素将会发生什么变化。

.nested {
    display: contents;
}

如果你使用的是Firefox或Chrome Canary浏览器,你将看到.nested里的元素现在开始直接参与Grid和Flex布局。因为.nested容器的盒子模型已经消失了,这意味着他们的行为就像Flex和Grid容器的子元素一样。

然而,他们并没有成为直接的子元素,我们使用一个选择器,只针对子元素添加了边框和flex属性。而嵌套的元素并没有边框,Flex项目也没有flex:1样式,而只是使用了默认的flex属性。

你可以看到使用display:contents;元素的边框消失了,那是因为元素的盒模型不存在了。也就是说你不能将背景和边框应用于使用了display:contents的元素上。这就是为什么display:contents不能弥补display:subgrid的一个重要原因。如果我们回到前面的示例中,我们想要排队,我们可以在.card上使用display:contents,它就成为网格容器的子元素。

.card {
    border: 4px solid rgb(24,154,153);
    background-color: #fff;
    display: contents;
}

然而,当我们使用自动放置我们的卡片时,一旦外盒被移除,每个卡片的内部项目都将直接参与到网格布局中,因此会自动放置。我们现在在第一个网格单元格中放置的是标题、然后是图像,接下来是内容,而footer被移到了下一行中。接下是另一个卡片内容的开始排列,依此类推。

你现在可以把这些东西改成列,然后把它们堆起来,这样你只需要决定每列中要有多少行就行了。

为了接近我们所希望的布局,我们南非要将每个项目放置在网格上,确保我们的卡片的每个部分都放置在我们想要的位置。下面就是第一个卡片每个内容对应的CSS:

.card:nth-child(1) h2{ 
    grid-column: 1; 
    grid-row: 1; 
}
.card:nth-child(1) img{ 
    grid-column: 1; 
    grid-row: 2; 
}
.card:nth-child(1) .inner{ 
    grid-column: 1; 
    grid-row: 3; 
}
.card:nth-child(1) footer{ 
    grid-column: 1; 
    grid-row: 4; 
}

你可以在下面的CodePen中看到完整的示例效果,不过你需要使用Firefox或Chrome Canary浏览器。

注意,这实际上并没有得一我们想要的。每个卡片已经失去了背景、边框和间距。我们也已经失去了自动放置和拥有尽可能多的卡片的能力。display:contents不是很好,这正是我们在网格布局中为什么需要子网格功能的原因所在。

什么时候应该使用display:contents

一旦在所有浏览器都支持display:contents;,我认为很有用。使用它时,你可以删除元素的盒模型样式,比如背景、边框等。它可以帮助我们保持良好的语义化结构,同时也能帮助我们得到我们想要的布局。同时我们也希望浏览器也具备子网格特性,因为在这两个特性之间,我们将为我们的网格布局增加更强的能力。

为了帮助和鼓励更多的浏览器支持display:contents,开发者应该发出这方面的声音,你也可以给Edge浏览投赞成票,让其也能早日支持这个特性。子网格特性已经被移到CSS Grid Layout Level2中。如果你有关于子网格的想法和用例,那么请把它们写出来。如果你把这些示例发给我,我将把它们添加到我的列表中。请记住,规范是在早期阶段,有机会让你的需求成为讨论的一部分,并希望通过最终的规范来解决。

本文根据@Rachel Andrew的《Why display: contents is not CSS Grid Layout subgrid》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://www.rachelandrew.co.uk/archives/2017/07/20/why-display-contents-is-not-css-grid-layout-subgrid/

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/css3/why-display-contents-is-not-css-grid-layout-subgrid.html