CSS之Grid布局

2022年10月05上次更新于 大约 1 个月前
编程

网格布局(Grid)被视为当前最强大的CSS布局方案。

1. 前言

我需要的布局大致如下:

可以看到,此布局并不复杂,我们可以使用多种方式来实现它,但今天我的计划是使用Grid布局。

Grid布局将容器划分为“行”与“列”,产生单元格,然后指定“item”所在的单元格,因此也常被视为“二维布局”。

2. 基本概念

2.1 容器和项目

如名所示,最外层的元素作为容器(container),内部每一个最外层的元素作为一个单独的项目(item)

<main>
  <section>a</section>
  <section>b</section>
  <section>c</section>
</main>

Grid布局针对main生效,section作为item,其内部元素与布局无关。

2.2 行和列

我想这一张图已经非常明显地使用深色体现出行(row)与列(column)的区别,行和列是有交叉的。

2.3 单元格和网格线

行列交叉的区域,我们称之为Cell(单元格),如前言所示,我们将子元素放在单元格中。而深色区域,我们将之称为Grid line(网格线),通常nm列,即可产生可供布局的n*mCell

不要讲空白区域视为单元格,单元格始终是相交产生的。

3. 容器属性和项目属性

Grid布局属性分为定义在container上的容器属性,定义在item上的项目属性

3.1 容器属性

3.1.1 display

显示为container设置display: grid显示属性布局为grid

div {
  display: grid;
}

此时,container是一个单独的容器,默认是块级元素,也可以设置display: inline-grid为行内Grid布局,使其整体视为一个行内块级元素。

网格布局将使得子项(item)的floatdisplay: inline-blockdisplay: table-celldisplay: vertical-aligndisplay: column-*等设置失效。

3.1.2 grid-template-rows 、grid-template-columns

grid布局除了需要显示指定布局类型为grid外,还需要指定行和列的值。

grid-template-rows定义行高,有多少行就提供多少个值。

grid-template-column定义列宽,同样,有多少列就提供多少个值。

例如,如果我们要设置一个九宫格,则分别需要三行三列:

.container {
  display: grid;
  grid-template-rows: 100px 100px 100px;
  grid-template-column: 100px 100px 100px;
}

如此一来配上item(css 提供一些颜色值):

<div id="container">
  <div class="item item-1">1</div>
  <div class="item item-2">2</div>
  <div class="item item-3">3</div>
  <div class="item item-4">4</div>
  <div class="item item-5">5</div>
  <div class="item item-6">6</div>
  <div class="item item-7">7</div>
  <div class="item item-8">8</div>
  <div class="item item-9">9</div>
</div>

我们可以得到一个九宫格布局:

除了使用px这样的绝对单位,也可以使用百分数,甚至可以使用repeat类函数简化赋值:

.container {
  display: grid;
  grid-template-columns: repeat(3, 33.33%);
  grid-template-rows: repeat(3, 33.33%);
}

甚至是:

.container {
  display: grid;
  grid-template-columns: repeat(2, 100px 40px 50px);
  grid-template-rows: 50px 50px 50px;
}

定义了100px 20px 80px 100px 20px 80px,6 列宽度不一的列。

image-20210610012604962

如上所示,第三行由于没有item,默认空白。

某些场合下,我们希望容器尽可能填充每一行的item,可以使用auto-fill关键字:

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, 100px);
}

image-20210610012923160

容器根据最大宽度进行自动列填充,此时行与列的数量是根据宽度变化的。

某些场合下,我们希望动态根据片段比例对行数进行判断,次数可以使用fr(fraction)关键字,表示列的宽度片段,例如:

.container {
  display: grid;
  grid-template-columns: 2fr 1fr;
}

上述示例表示,第一列宽度为整个容器宽度的2/3,第二列为1/3,一般配合绝对宽度使用可以实现很灵活的布局效果:

.container {
  display: grid;
  grid-template-columns: 150px 1fr 2fr;
}

上述示例,每一行先扣除第一列的150px宽度,剩下的再动态计算分配。也可以使用auto关键字,由浏览器决定长度。

grid-template-columns: 100px auto 100px;

网格线可以具有名字,并且可以有多个名字(使用中括号括起来),方便后续复用。

.container {
  display: grid;
  grid-template-columns: [c1] 100px [c2] 100px [c3] auto [c4];
  grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];
}

3.1.3 gap

网格线间距属性gap,其属性为行和列的简写:

.container {
  gap: <row-gap> <column-gap>;
  gap: 20px 20px;
}

如果简写忽略了第二个值,则默认等于第一个值。

3.1.4 grip-template-areas

网格布局可以通过字符串,抽象画的划分不同item所属的区域。

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
  grid-template-areas:
    "a b c"
    "d e f"
    "g h i";
}

.area-a {
  grid-area: a;
  ....;
}

grid-template-areas通过空格将不同区域分割开来,然后可以在css中直接使用grid-area属性和区域名作为值,再为标签添加类即可针对性的设置样式。

不使用的区域可以使用.占位,可以不同cell具有相同的area名,以便于指定样式,例如:

grid-template-areas:
  "a . a"
  "b . b"
  "c . d";

grip-template-rows可以定义子项高度,同时也可以为网格线命名,而网格线可以有多个名字。grid-template-areas指定区域名的时候,也默认生成了areaName-startareaName-end这样的网格线别名。

3.1.5 grid-auto-flow

容器划分好网格后,容器内item按顺序放置,默认先行后列,这个顺序是可以更改的。grid-auto-flow就是设置这个顺序的属性,默认值为row,先行后列,如果需要先列后行,则设置值为column

当某行或者某列按次序放置子项的时候,存在剩余宽度不足的情形,如果需要可以在rowcolumn后添加一个dense值,二者用空格分开,意为尽可能让子项连续密集显示,如此一来就会跳过宽度超过剩余宽度的子项,按序优先使用后续满足条件的子项。例如:

属性:

grid-auto-flow: row dense;

结果:

对于某些严格需要避免中间空白的布局来说,这个属性非常有效。

3.1.6 单元格位置

justify-items设置单元格水平布局,align-items设置单元格垂直布局,二者可选的值为:

  • start
  • end
  • center
  • Stretch (默认值,拉伸)

如果要设置整个容器内的单元格位置,也就是将容器内所有单元格视为一个整体,其布局属性可用:

  • justify-content::整体水平对齐
  • align-content: 整体垂直对齐
  • place-content:此为上述两个属性的简写方式,如果忽略第二个值则采用第一个值

这几个布局属性的值类似flex的布局值,分别是:

  • start
  • end
  • center
  • stretch(拉伸)
  • Space-around 项目两侧间隔相等,子项之间距离两个间隔
  • Space-between 子项之间距离相等,第一个子项和最后一个子项左边或右边没有空白,紧贴容器
  • Space-evenly 子项左右空白距离相等

3.1.7 grid-auto-rows and grid-auto-columns

容器网格只有三行的时候,如果需要指定某个子项在第五行,这时候浏览器自动根据子项大小创建新的网格以放置额外的子项,我们可以通过grid-auto-rowsgrid-auto-columns指定自动创建的网格的高度和宽度。

例如:

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
  grid-auto-rows: 50px;
}

之所以89会占据图中的位置,是因为我们使用css指定其行和列的位置值:

.item-8 {
  background-color: #d0e4a9;
  grid-row-start: 4;
  grid-column-start: 2;
}

.item-9 {
  background-color: #4dc7ec;
  grid-row-start: 5;
  grid-column-start: 3;
}

由此引出grid-row-startgrid-column-start属性,可以指定其元素的位置。

除了start还有end可以指定,看示例:

.item-1 {
  grid-column-start: 2;
  grid-column-end: 4;
}

此时如果没有指定grid-auto-flow: row dense;,则会让布局看起来如下图所示:

为了方便记忆,可以将网格线数字改为网格线名。

这四个属性的值还可以使用span关键字,表示"跨越",即左右边框(上下边框)之间跨越多少个网格。

.item-1 {
  grid-column-start: span 2;
}

3.1.8 属性简写

此前我有翻译过google html & css guide文档风格指南,其中有一条建议是尽量在css中使用简写,我认为这是一个很好的准则。

grid-template属性是grid-template-columnsgrid-template-rowsgrid-template-areas这三个属性的合并简写形式。

grid属性是grid-template-rowsgrid-template-columnsgrid-template-areasgrid-auto-rowsgrid-auto-columnsgrid-auto-flow这六个属性的合并简写形式。

如果你喜欢简写,务必不要弄错简写的属性顺序。

grid-column属性是grid-column-startgrid-column-end的合并简写形式,grid-row属性是grid-row-start属性和grid-row-end的合并简写形式。

.item {
  grid-column: <start-line> / <end-line>;
  grid-row: <start-line> / <end-line>;
}

下面是一个例子。

.item-1 {
  grid-column: 1 / 3;
  grid-row: 1 / 2;
}
/* 等同于 */
.item-1 {
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 1;
  grid-row-end: 2;
}

上面代码中,项目item-1占据第一行,从第一根列线到第三根列线。

这两个属性之中,也可以使用span关键字,表示跨越多少个网格。

.item-1 {
  background: #b03532;
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
/* 等同于 */
.item-1 {
  background: #b03532;
  grid-column: 1 / span 2;
  grid-row: 1 / span 2;
}

上面代码中,项目item-1占据的区域,包括第一行 + 第二行、第一列 + 第二列。

grid-area属性还可用作grid-row-startgrid-column-startgrid-row-endgrid-column-end的合并简写形式,直接指定项目的位置。

.item {
  grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
}

.item-1 {
  grid-area: 1 / 1 / 3 / 3;
}

效果如上图所示。

3.2 子项属性

子项和容器的属性可以拆分开来,通过诸如justify-self等带self关键字的属性控制单独的子项的样式,并且优先级高于容器上相关的样式属性。

3.2.1 justify-self 、align-self、place-self

justify-self属性设置单元格内容的水平位置(左中右),跟justify-items属性的用法完全一致,但只作用于单个项目。

align-self属性设置单元格内容的垂直位置(上中下),跟align-items属性的用法完全一致,也是只作用于单个项目。

.item {
  justify-self: start | end | center | stretch;
  align-self: start | end | center | stretch;
}

place-self属性是align-self属性和justify-self属性的合并简写形式。

place-self: center center;

如果省略第二个值,place-self属性会认为这两个值相等。

4. 解题

学习了grid布局的知识后,让我们将之运用到一开头我的需求中来,再次看这个图:

针对性的容器CSS如下:

.container {
}

参考

not-by-ainot-by-ai
文章推荐

Friends

Jimmy老胡SubmaraBruce SongScarsu