所有奇技淫巧都只在方寸之间。
几乎从我们踏入前端开发这个领域开始,就不停地接触不同的布局技术。从常见的浮动到表格布局,再到如今大行其道的flex布局,css布局技术一直在不断地推陈出新。其中网格布局(grid)作为css3的产物,它更加贴近网页设计师所使用的布局策略,学习并利用好它可以让我们免受很多布局困扰。
虽然网格布局好处有很多,但学习起来并不简单,原因是用来设置布局的属性实在太多,其中光是作用于父容器的属性就有17种,再加上子元素属性有10种,另外还有这些属性值的不同取值方式。这些对于记忆来说绝对是个不小的负担。那么这么多属性以及用法,要如何在短时间内消化掉呢?在接下来这篇文章里,我将针对这27种属性以及它们各自的用法,分享我独家的学习策略,希望对大家的学习有所帮助。
布局之道
CSS作为一种网页排版设计语言,其核心的设计思想必然要遵守相关的领域知识。网格布局是一种二维布局结构,它是由纵横相交的两组网格线形成的框架性布局结构。网页设计者可以利用这些由行(row)和列(column)形成的框架性结构来布局设计元素。 在定义一种网格布局结构的时候,我们需要在父容器上描述要布局的主体框架结构。为了描述这一框架结构,我们就需要给它的基本构成元素命名。一个网格布局的构成元素可以概括为以下几种概念:
- row line: 行线
- column line: 列线
- track: 网格轨道,即行线和行线,或列线和列线之间所形成的区域,用来摆放子元素
- gap: 网格间距,行线和行线,或列线和列线之间所形成的不可利用的区域,用来分隔元素
- cell: 网格单元格,由行线和列线所分隔出来的区域,用来摆放子元素
- area: 网格区域,由单个或多个网格单元格组成,用来摆放子元素
牢记上述这些概念是之后熟练掌握和应用网格布局的基础。
构建之法
要熟练掌握一门技术,核心是找到最基本的套路,然后不断练习从而可以在之后的实践过程中减少决策的时间。所以,这一部分主要就是介绍网格布局构建过程中的一些常用套路。 这里我们要解决的问题是,如何利用最基本的规则来构建出理想的布局模型。在布局过程中,归根结底需要处理的就两种页面元素:父容器和子元素。前者主要用来设置基础的布局框架,相当于建筑中的设计蓝图,而后者就是用来进行个性化的布局调整。因此我个人归纳了在使用网格布局过程中的套路是:针对父容器元素进行设置需要三个步骤:定框架、设间隔和找对齐,对子元素来说有两个步骤:摆位置和找对齐。我把它们统称为**"32构建之法"**。
在这一小节中,我将把重心主要放在网格布局中所有用到的27个属性名的讲解上,而取值逻辑将在最后一部分进行统一介绍。
父容器
定框架
设置父容器的网格布局的第一步就是将父容器的盒模型设置为grid
,这样就能触发渲染引擎的网格布局算法。
.parent {
display: grid;
}
接着我们要开始准备**"画线"**,即设置所需行和列的基础线。这些线条将构成我们接下来进行布局排布的基础模板(template)。在画线过程中,我们需要分别根据行(row)和列(column)两个维度进行设置。你需要画几条线,就设置几个值(不包括边框),其取值是轨道(track)的大小。这里我先画出一个3x3的网格框架,代码如下:
.parent {
display: grid;
grid-template-rows: 100px 100px 100px;
grid-template-columns: 100px 100px 100px;
}
在这里你也可以选择使用缩写形式同时为行和列设置值,采用/
分隔开:
.parent {
display: grid;
grid-template: 100px 100px 100px / 100px 100px 100px;
}
画完线后,下一步我们可以选择为这些线条和线条之间形成的网格区域(area)进行命名,这样在后续使用的时候就能直接使用这些名字,便于子元素的定位。
.parent {
display: grid;
grid-template-areas: "a a b"
"c d e"
"c d ."
}
上面这两步画线和命名同样可以采用缩写形式进行设置,代码如下:
.parent {
display: grid;
grid-template: "a a b" 101px
"c d e" 102px
"c d ." 103px / 104px 105px 105px
}
因为使用grid-template
同时设置行列和区域名的写法比较复杂,为了讲解方便,我把值设置成规律的递增数字。其中(101, 102,103)
设置的是grid-template-rows
的值,而(104,105,106)
设置的则是grid-template-columns
的值。
到这一步,我们可以说已经完成所需工作的一大半了。
设间隔
间距(gap)的设置在实际开发中是可选的,主要根据网页设计的需求而定。如果你需要给网格线之间设置间距,我们可以在行列两个维度上分别进行设置, 下面这段代码将给每个行和列分别设置10px的间隔:
.parent {
display: grid;
grid-template: 100px 100px 100px / 100px 100px 100px;
grid-row-gap: 10px;
grid-column-gap: 10px;
}
如果采用缩写形式,上述代码又可以简化成:
.parent {
display: grid;
grid-template: 100px 100px 100px / 100px 100px 100px;
grid-gap: 10px 10px;
}
设置后的效果如下:
找对齐
有了前面两个步骤,我们的网格布局框架基本上算是搭建得差不多了。每个子元素都会默认占据一个网格区域。而在父容器这里我们如果有需要,就要进行最后一个步骤:找对齐。所谓对齐方式,可以分为内部和外部两种(前者是针对每个网格区域的子元素而言,而后者是相对于网格区域本身)。另外在行和列(更专业的术语是main axis和cross axis)上又各自有两个维度,这就构成了4种设置对齐的方式。
先来处理一下每个子元素相对网格区域内部的对齐方式:
.parent {
display: grid;
grid-template: 100px 100px 100px / 100px 100px 100px;
grid-gap: 10px 10px;
justify-items: center;
align-items: center;
}
在上面的代码中我分别在行和列方向上都设置了居中对齐,这样每个网格区域中的子元素相对于各自的区域行为是一致的,都能均匀排布。可以看到效果如下图所示:
再来看一下另一种情况:
.parent {
display: grid;
width: 500px;
height: 500px;
grid-template: 100px 100px 100px / 100px 100px 100px;
grid-gap: 10px 10px;
justify-content: space-between;
align-content: center;
}
有时候我们设置的网格不足以覆盖整个父容器的大小时,比如在上述的例子中整个父容器有500px*500px
的大小,而我们只设置了300px*300px
的网格区域,这时候就需要指定多出来空间的处理规则。justify-content
和align-content
就是分别在行和列两个方向上用来解决这个问题的属性。它们将针对每个网格区域去设置其在父容器中的对齐方式。
justify
和align
这两个单词在方向上比较容易搞混,所以我在记忆上采取的方式是记住justifyrow
和aligncolumn
这两个合并词,它们长度差不多。如果你有更好的记忆方式,请留言告诉我。
子元素
我们通过在父容器上搭建好了基础的框架后,对于大部分子元素来说,就已经能够很好地满足布局要求了。针对部分子元素,可以根据需求进行微调。如果要在子元素上进行布局微调,通常需要以下两个步骤:摆位置和找对齐。
摆位置
像下棋一样,针对子元素的排布,我们需要给它们指定要摆放的具体位置。要确定具体位置,可以利用之前在父容器中所指定的线名和区域名来定位。一种方式是直接通过设置起始行,结束行和起始列,结束列来给子元素划定它所要摆放的区域,另外一种方式是指定要摆放的区域名。
/* 指定起始行,结束行,起始列,结束列 */
.child:first-child {
grid-row-start: 1;
grid-row-end: 2;
grid-column-start: 1;
grid-column-end: 3;
background: red;
}
/* 使用缩写形式 */
.child:nth-child(2) {
grid-row: 2/3;
grid-column: 2/4;
background: yellow;
}
/* 直接指定区域名 */
.child:nth-child(3) {
grid-area: i;
background: green;
}
这段代码的效果如下:
还有一种更加灵活的设置位置方式,是指定跨越的行数和列数,关键字span
用来控制一次跨越的行数或列数, 如上面第三个子元素可以改写成:
.child-nth-child(3) {
grid-row: 2/3;
grid-column: span 2;
}
找对齐
和父容器中所设置的对齐方式类似,针对个别子元素的对齐处理,我们可以按照行列两组属性进行分别处理:
/* 列对齐 */
.child:nth-child(1) {
align-self: end;
}
/* 行对齐 */
.child:nth-child(2) {
justify-self: end;
}
/* 采用缩写形式 */
.child:nth-child(3) {
place-self: center center;
}
*隐式网格
灵活性是网格布局的一大优势,除了采用上述那种手动指定框架结构的方式,网格布局还有一套自动化布局的机制,这套机制称为**“隐式网格布局”**。当我们在网格定义的区域外放置子元素时,或因子元素数量过多而需要更多的网格线时,布局算法就会自动生成隐式网格。默认情况下这些隐式网格的大小也会随着内容尺寸不同而变化,而我们可以利用属性grid-auto-rows
和grid-auto-columns
来控制隐式网格的大小。 考虑下面这个例子:
<div class="parent">
<div class="child" style="background: red"></div>
<div class="child" style="background: yellow"></div>
<div class="child" style="background: green"></div>
</div>
.parent {
display: grid;
grid-auto-rows: 100px;
grid-auto-columns: 100px;
}
通过手动在父容器中设置隐式网格大小为100x100的大小后,效果如下: