原文作者:Rachel Andrew
原文链接:https://www.smashingmagazine.com/2020/01/understanding-css-grid-container/
译者注:
CSS Grid布局 (又名"网格"),是一个基于二维网格布局的系统,旨在改变我们基于网格设计的用户界面方式。我们常用的Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局,并不适用于复杂的二维布局。网格(Grid)是第一个专门为解决布局问题而创建的CSS模块,用来解决我们之前在制作网站时使用各种复杂处理的布局问题。
在进入正文之前为了防止翻译上存在一些歧义或者不准确的地方,大家先了解几个关于网格布局的关键术语。
好了,下面正式开始:
概要:在一个新的系列中,Rachel Andrew将会向我们介绍css网格的规范。在本次文章中,我们将会详细了解当我们创建了一个网格容器的时候会发生什么,以及各种各样可以应用在网格容器上的属性。
这是Smashing Magazine上面关于网格布局系列文章的开篇。尽管网格布局在2017年浏览器就已经支持使用了,但是很多开发者还是没有机会在项目中使用它。似乎有很多新的属性和值与CSS网格布局相关。这使得它势不可挡。然而,它的很多规范细节都可以使用不同的方式来实现,这意味着你在没有完全了解规范的情况下,也可以开始使用。本系列旨在将您从网格布局的新手带到专家:一路上有很多实用的使用技巧交给大家。
开篇的文章将会包括:你在创建一个网格容器时将会发生什么,以及各种你可以应用在父元素上的属性以控制网格。您将发现,有几个用例仅在将属性应用在网格容器上时才会生效。
这篇文章中,我们包括:
- 使用display: grid或者display: inline-grid创建网格容器
- 使用grid-template-columns 和 grid-template-rows属性设置列和行
- 使用grid-auto-columns 和 grid-auto-rows控制内含尺寸
创建一个网格容器
grid就像flex布局一样,是一种display属性。所以使用display: grid来告诉浏览器你想使用网格布局。之后,设置了网格属性的元素将成为一个块级元素,它里面所有的直接子元素都将处于一个网格格式上下文中。这就表明,它们都将表现出网格元素的性质,而不是普通的块级或者行内元素。
也许,你还没有在你的页面上看到差别。那是因为你还没有创建一个行或者列。尽管已经默认创建了一个行,并包含你所有的子元素。这些子元素在一列中上下排布。表现出的行为看起来就像块级元素。
如果有任何文本字符串(未包装在元素中)即是网格容器的直接子级,你就会看到不同,因为字符串将包装在匿名元素中并成为网格项。任何行内元素,比如span,只要直接处于网格容器中,就会表现出网格项的属性。
下面的示例有两个块级元素,在字符串中添加一个包含在span中的文字字符串。我们最终得到5个网格项:
- 两个div元素
- span前面的文本字符串
- span
- span之后的文本字符串
// html
<div class="grid">
<div>Item one</div>
<div>Item Two</div>
A string of text with a <span>span element</span> in the middle.
</div>
This string of text follows the grid.
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
最后的表现:
如果使用Firefox的grid Inspector检查网格,可以看到为为每个网格项创建的5行轨迹。
你也可以通过display: inline-grid创建一个行内网格。在这个例子中,你的网格变成了一个行级盒子。但是,直接子项仍然是网格项,其行为方式与块级框中的网格项相同(它只是外部显示类型)。这就是为什么当网格容器与页面上的其他框并排时,它的行为与上面的相同。
下一个示例有一个网格,后面跟着一个文本字符串,因为这是一个内联级别的网格,文本可以与它一起显示。内联级别的内容不会像块级别的内容那样拉伸以占用内联维度中的所有空间。
<div class="grid">
<div>Item one</div>
<div>Item Two</div>
A string of text with a <span>span element</span> in the middle.
</div>
This string of text follows the grid.
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: inline-grid;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
最后的表现:
列和行
为了使布局看起来像网格,你需要添加行和列。我们使用grid-template-columns 和 grid-template-rows属性来创建。在规范中有一个定义叫做网格轨道track-list。
这些属性指定了行的名字以及网格的追踪尺寸函数。网格模板列属性grid-template-columns指定网格列的尺寸跟踪方法,而网格模板行grid-template-rows指定网格行的跟踪函数。
下面列出了一部分有效影响轨道尺寸的值:
属性 | 作用 |
---|---|
grid-template-columns: 100px 100px 200px; | 创建三列网格:第一列是100px,第二列是100px,第三列是200px |
grid-template-columns: min-content max-content fit-content(10em) | 创建三列网格:第一列追踪min-content尺寸,第二列是max-content,如果容器大小超过10em,第三列也是max-content,否则就撑到10em |
grid-template-columns: 1fr 1fr 1fr; | 使用fr创建了三列网格,这三列将平分容器尺寸 |
grid-template-columns: repeat(2, 10em 1fr); | 使用重复命令创建了尺寸分别为10em 1fr 10em 1fr 的四列网格 |
grid-template-columns: repeat(auto-fill, 200px); | 创建尽可能多的宽为200px的列,如果剩余控件不足200px,就留下空白 |
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | 创建尽可能多的宽为200px的列,如果剩余控件不足200px,已创建的列将平均分配剩余空间。 |
grid-template-columns: [full-start] 1fr [content-start] 3fr [content-end] 1fr [full-end]; | 创建三列网格:第一列和第三列各有一份可用空间,而中间列占三份。将行名称放在方括号中对行进行命名。 |
如您所见,有很多方法可以创建网格轨道(track list)!让我们来看看这些都是如何工作的,并给出一些提示,说明为什么要使用每这些属性。
使用长度单位
你可以使用任何长度单位或者百分比创建你的网格轨道(tracks)。如果网格轨道(tracks)的大小加起来小于网格容器中可用的大小,则默认情况下,网格轨道(tracks)将会从左到右排列,剩余部分就会空白。这是因为align-content 和 justify-content这两个属性的默认值是start。你可以使用校准元素将元素分隔开或者移动到容器最后面。详细的介绍可以看我的这篇文章“How To Align Things In CSS”.。
<div class="grid">
<div>Item One</div>
<div>Item Two</div>
<div>Item Three</div>
<div>Item Four</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: 100px 200px 100px;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
你也可以使用min-content, max-content 和 fit-content()属性。使用min-content属性,会创建尽可能小的网格轨道(tracks)来避免出现溢出的情况。所以,当作为列的属性时,会尽可能的折叠里面的内容。这时网格轨道(track)的高度就变成了容器中最长单词或最大固定大小元素的大小。
使用max-content属性不会引起任何折行。在一列中,文本字符不会折行,这可能会引起溢出。
fit-content是css函数,只能通过传值来使用。这个值就是网格轨道所能增长到的最大值。它的表现就像max-content一样,会随着内容的不断变长而变宽。但是当内容长度超过这个值以后,里面的元素就开始折行。所以,这个网格轨道有可能小于你所传的值,但不会比它大。
<div class="grid">
<div>Item One</div>
<div>Item Two Item Two</div>
<div>Item Three Item Three Item Three</div>
<div>Item Four</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: min-content max-content fit-content(10em);
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果
你可以在我的这篇文章How Big Is That Box? Understanding Sizing In CSS Layout中发现更多关于网格尺寸和布局的方法。
如果你的tracks占用的空间超过你的容器,它们就会溢出。如果你使用百分比,就与基于百分比的浮动或弹性布局一样,如果想要避免溢出,就要注意总和不要超过100%。
fr 单位
网格布局包括一种方法可以让你避免自己计算百分比-就是使用fr。它并不是一个值,所以不能和calc()一起使用。它是一个弹性单位,表示网格中的可用空间。
这就表示,当我们1fr 1fr 1fr;设置时,剩余空间会被分成三分并平分空间。当使用2fr 1fr 1fr,空间会被分成四分,其中两份被分配给第一个网格轨道,剩下两个网格轨道分别占据一份。
<div class="grid">
<div>Item One</div>
<div>Item Two Item Two</div>
<div>Item Three Item Three Item Three</div>
<div>Item Four</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: 2fr 1fr 1fr;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
最终渲染:
需要注意的是,默认情况下共享的是可用空间,而不是容器中的总空间。如果你的任意一个网格轨道中包含规定尺寸的元素或者一个无法被折行的文本,它会在空间被分配之前布局。
在一下个例子中,我将去掉第三列中字符的字间距。这样就形成了一个长的不可折断的字符串,所以,在进行网格布局之前会优先处理字符串的布局,将它所在的列撑开了。然后剩余的两列按照2:1的比例进行布局。
<div class="grid">
<div>Item One</div>
<div>Item Two Item Two</div>
<div>ItemThreeItemThreeItemThree</div>
<div>Item Four</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: 2fr 1fr 1fr;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
你可以将固定尺寸的网格轨道和fr一起使用。例如你可以创建一个容器,包含两个固定尺寸的列,两个列中间区域自适应。
<div class="grid">
<div>Fixed</div>
<div>Flexible</div>
<div>Fixed</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: 100px 1fr 100px;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
另外,你也可以使用fit-content(300px)创建第一列,剩余得部分用fr处理。这样容器的第一部分就会根据内容占据所需要的空间。fr单位的列会扩大占据剩余部分。
如果元素中的内容放入更大的内容(比如设置属性max-width: 100%的图片),第一列最大宽度只能到300px。fr单位的列占据剩余部分。
将fr和fit-content一起使用也是让你的网站更加灵活的一种方式。
<div class="grid">
<div><svg width="24" height="24" viewBox="0 0 24 24">
<path d="M23,12L20.56,9.22L20.9,5.54L17.29,4.72L15.4,1.54L12,3L8.6,1.54L6.71,4.72L3.1,5.53L3.44,9.21L1,12L3.44,14.78L3.1,18.47L6.71,19.29L8.6,22.47L12,21L15.4,22.46L17.29,19.28L20.9,18.46L20.56,14.78L23,12M13,17H11V15H13V17M13,13H11V7H13V13Z"></path>
</svg></div>
<div>This side is flexible</div>
</div>
<div class="grid">
<div><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/balloon-sq4.jpg" alt="balloons"></div>
<div>This side is flexible</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
margin: 1em 0;
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: fit-content(300px) 1fr;
width: 600px;
}
img {
max-width: 100%;
display: block;
}
渲染结果:
REPEAT()方法
使用repeat()函数创建网格轨道可以很方便的创建重复属性的列,避免一遍又一遍的输入同样的值。比如,下面两列的作用是一样的。
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(12, 1fr);
repeat()函数的第一个参数是要创建的网格轨道个数,第二个参数为列的尺寸,用逗号隔开。
你也可以将repeat()作为一部分参数,下面的例子中,创建了一个1fr的列,三个宽为200px的列,最后一列为1fr。
grid-template-columns: 1fr repeat(3,200px) 1fr
另外,函数传入一个值表示创建列的个数,但也可以使用auto-fill or auto-fit。使用这两个关键词意味着创建列的个数并不是固定的,而是根据容器的宽度,创建尽可能多的列。
<div class="grid">
<div>Item One</div>
<div>Item Two</div>
<div>Item Three</div>
<div>Item Four</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
如果创建列的宽度是固定的,那么除非容器正好能被列进行平分,否则就会留下空白部分。比如我的容器宽度为500px,就会创建两个200px的列和100px的空白。
针对上面的问题,可以使用另外一个网格函数定义最小值,剩余的空间可以被所有已创建的列进行平分。minmax()定义最小值和最大值。比如定义列最小为200px,最大为1 fr,那么就会创建尽可能多的宽为200px的列来填充容器。因为最大值为1fr,所以如果有剩余空间,就会被已创建列平分。
<div class="grid">
<div>Item One</div>
<div>Item Two</div>
<div>Item Three</div>
<div>Item Four</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
上面,我提到了两个关键词auto-fill and auto-fit。如果第一行有足够的内容去填充,那么他们的表现是一样的。但是如果没有(比如删除其它内容只剩下一列),那么它们的表现就会有所不同。
使用auto-fill会保留剩余空间,尽管没有内容填充了。
<div class="grid">
<div>Item One</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
但是auto-fit会将剩余空间折叠。
<div class="grid">
<div>Item One</div>
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
渲染结果:
通过Firefox Grid Inspector,可以看到,第二列仍然存在,只不过宽度变为了0。我们可以看到第三条网格线,这说明容器中存在两条网格轨道。
命名行
我最后说一下行的命名。当使用网格的时候,你可以获得每行行数,现在你也可以给每一行命名。将行的名字放在中括号里。也可以为行定义多个名称,用空格分开。下面的例子中,每个行我都定义了两个名字。
grid-template-columns: [main-start sidebar-start] 1fr [sidebar-end content-start] 4fr [content-end main-end]
除了单词span,你可以为行定义任何您喜欢的名称,因为span是一个保留字,在网格上放置项目时使用它。
注意:本系列后续文章中,我将更多地讨论基于行的布局以及如何使用命名行。同时,我的这篇文章“Naming Things in CSS Grid Layout”以帮助你更好的理解这些。
显式vs隐式网格
当使用grid-template-columns 和 grid-template-rows属性定义时,你就是很明确的显式定义了网格。每一个网格轨迹都是根据你的赋值创建的。
当你定义的轨道数量超过容器的空间,或者将一个单元放在创建的网格之外,将创建隐式网格轨道。这些隐式轨道默认自适应大小。比如,我在父元素上声明display:grid,它包含的每一项都会创建一个行轨道。尽管我并没有去定义row,但是因为它们变成了网格布局中的元素,为了放置这些元素,就会自动创建出行轨道。
你可以使用grid-auto-rows 或者 grid-auto-columns属性设置隐式行或者列的尺寸。如果你希望所有的隐式列都至少有200px的宽,并随着内容的增大而增大,你可以这样设置:
grid-auto-rows: minmax(200px, auto)
如果你希望第一个隐式行自适应大小,第二行为min-content直到所有网格项都被容纳。你可以传多个值:
grid-auto-rows: auto 100px
<div class="grid">
<div>Item one</div>
<div>Item Two</div>
A string of text with a <span>span element</span> in the middle.
</div>
body {
padding: 50px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
.grid {
border: 5px solid rgb(111,41,97);
display: grid;
grid-auto-rows: auto 100px;
width: 500px;
}
.grid > * {
background-color: rgba(111,41,97,.4);
border-radius: 5px;
padding: 10px;
}
使用具有自动放置功能的网格
创建一个网格(并允许浏览器自动放置元素)可以让您实现非常有价值的模式方面有很长的路要走。我们还没有设计在网格上放置项目,但是许多情况下网格的布局不需要进行任何放置。容器的子元素只是简单地按照代码中的顺序自动放置在每一个网格中。
如果你刚刚接触css网格,那么尝试使用不同方式定义网格轨迹并查看元素是如何放置的,是一个很好的开始。
END
共同学习,写下你的评论
评论加载中...
作者其他优质文章