前言
已经2022年了,相信各位开发同学都已经听说过CSS的grid布局了吧。
什么是Grid布局
Grid布局是一种二维的布局系统,简单来说就是在行和列的维度上进行布局,因此我们主要会碰到行(row)、列(column)、间距(gutter或者gap) 这几个概念。
下图来自MDN,可以看到对这些概念的标注。
下图是目前CSS grid布局的支持情况,可以看到高达95.81%的浏览器已经支持了这一布局。
跟着例子🌰快速尝试
构造4行3列(目标:构造grid布局、了解grid-template-columns和grid-template-rows、知道auto的用途)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 3列,第1列和第3列有固定宽度,中间的第2列自动占满剩余宽度 */
grid-template-columns: 80px auto 100px;
/* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
grid-template-rows: 25% 100px auto 60px;
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
效果如下图所示:列方向上分为3列,第1列和第3列有固定宽度,中间的第2列自动占满剩余宽度,行方向上4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px。
自动构造等宽的多列(目标:了解repeat、fr单位)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 自动循环生成3列 */
grid-template-columns: repeat(3, 1fr);
/* 可以试试这一句看看有什么效果 */
/* grid-template-columns: repeat(3, 1fr 1fr); */
/* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
grid-template-rows: 25% 100px auto 60px;
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
效果如下图所示:repeat(3, 1fr)加粗的3指示把第二个参数重复3次。fr(fraction) 是grid布局里的一种单位,用于指示当前单元在grid容器内的可用空间中所占的分数,可以看到目前3列是等宽的。注意,repeat的第二个参数可以是多个空格分割的数据,如
repeat(3, 1fr 1fr)
,这样会形成3*2即6列。
设置了列数,不设置行数,会自动进行排布(目标:了解grid可以自动计算行数、grid-auto-rows属性、minmax函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 自动循环生成3列 */
grid-template-columns: repeat(3, 1fr 1fr);
/* grid-auto-rows可以设置高度,可以设置具体数值,也可以用minmax设置上下限 */
grid-auto-rows: minmax(100px, auto);
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child" style="color:red;">
<div>......................................................................................</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
<div>...</div>
</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<!-- <div class="child">...</div>-->
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
这里有11个子元素,设置了4列,可以看到自动生成了2行。
grid-auto-rows可以设置高度,可以设置具体数值,也可以用minmax设置上下限,这里由于设置了行的最小高度为100px,因此第1行第1列的子元素的高度在超过
父元素高度400px-第2行高度100px=300px
之后,溢出了
第1行第1列的子元素的宽度过大,会导致挤压右边的列
固定宽度、auto和fr单位混用(目标:了解宽度分配模式)
px
和fr
混用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 第1列占用固定宽度,另外两列自动分割剩余空间 */
grid-template-columns: 100px 1fr 1fr;
/* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
grid-template-rows: 25% 100px auto 60px;
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
效果如下图所示:第1列占用固定宽度,另外两列由于采用了
fr
单位,因此会自动分割剩余空间。
auto
和fr
混用,且fr
之和大于1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 第2列设置为auto,表现为包裹内容,另外2列自动按比例分配剩余空间 */
grid-template-columns: 1fr auto 1fr;
/* 也可以是 */
/* grid-template-columns: 0.5fr auto 0.5fr; */
/* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
grid-template-rows: 25% 100px auto 60px;
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
效果如下图所示:第2列设置为auto,表现为包裹内容,其宽度为14.65px(下面计算会用到这个数据),另外2列自动按比例分配剩余空间。
auto
和fr
混用,且fr
之和小于1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 第1列和第3列之和小于1 */
grid-template-columns: 0.4fr auto 0.5fr;
/* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
grid-template-rows: 25% 100px auto 60px;
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
效果如下图所示:第1列和第3列之和小于1,此时fr的自动计算是这样的,(容器宽度 - auto列内容的真实宽度)* fr值,在此处可以得到
(800-14.65)*0.4=314.14
,这里的14.65是从上面一个例子得到的。
不预设列数,自动根据子元素宽度进行填充
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
/* 自动根据子元素宽度填充 */
grid-template-columns: repeat(auto-fill, 150px);
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
<div class="child">...</div>
</div>
</body>
</html>
可以看到
repeat
的第一个参数使用了auto-fill
,意思是自动填充尽可能多的列,由于每一列的宽度是150px
,容器宽度为800px
,因此最后面剩余了50px
无法填充,只能自动换行
指定单元格位置、合并单元格(目标:学会grid-column和grid-row属性指定单元格位置,同时可以进行“合并单元格”的操作)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
grid-template-columns: repeat(auto-fill, 150px);
}
.child {
border: 1px solid black;
}
.merge1 {
grid-column: 2 / 4;
grid-row: 1 / 3;
}
.merge2 {
grid-column: 4;
grid-row: 2 / 4;
}
.merge3 {
grid-column: -3 / -1;
}
</style>
</head>
<body>
<div class="container">
<div class="child merge1">
child1 merge1
</div>
<div class="child merge2">
child2 merge2
</div>
<div class="child merge3">
child3
</div>
<div class="child">
child4
</div>
<div class="child">
child5
</div>
<div class="child">child6</div>
<div class="child">child7</div>
<div class="child">child8</div>
<div class="child">child9</div>
<div class="child">child10</div>
<div class="child">child11</div>
<div class="child">child12</div>
</div>
</body>
</html>
第1个子元素设置了
grid-column: 2 / 4;
,其中的2
代表上方的列分割线2,4
代表上方的列分割线4,因此其占据了2-3列。注意,分割线可以用负数进行标记,因此child3元素设置了grid-column: -3 / -1;
,即占据了最后两列。
通过命名单元格进行定位(目标:学会grid-template-areas)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
grid-template-columns: 1fr 3fr;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
}
.child {
border: 1px solid black;
}
.merge1 {
grid-area: header;
}
.merge2 {
grid-area: content;
}
.merge3 {
grid-area: sidebar;
}
.merge4 {
grid-area: footer;
}
</style>
</head>
<body>
<div class="container">
<div class="child merge1">
child1
</div>
<div class="child merge2">
child2
</div>
<div class="child merge3">
child3
</div>
<div class="child merge4">
child4
</div>
</div>
</body>
</html>
利用
grid-template-areas
属性定义不同区域的名称,然后在子元素中利用grid-area
来指定需要定位到的单元格名称。
利用Grid布局构建12/16/24列布局系统
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
grid-template-columns: repeat(12, minmax(0,1fr));
}
.child {
border: 1px solid black;
}
.merge1 {
grid-column: 1 / 13;
grid-row: 1;
}
.merge2 {
grid-column: 4 / 13;
grid-row: 2;
}
.merge3 {
grid-column: 1 / 4;
grid-row: 2;
}
.merge4 {
grid-column: 1 / 13;
grid-row: 3;
}
</style>
</head>
<body>
<div class="container">
<div class="child merge1">
child1
</div>
<div class="child merge2">
child2
</div>
<div class="child merge3">
child3
</div>
<div class="child merge4">
child4
</div>
</div>
</body>
</html>
过去实现类似布局,通常最方便的做法是使用框架的栅格系统,现在利用Grid布局也可以轻松实现类似的功能。
选择Grid布局还是Flex布局?
考虑如下的Flex布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flex</title>
<style>
.parent {
width: 450px;
display: flex;
flex-wrap: wrap;
}
.child {
height: 100px;
/* Three values: flex-grow | flex-shrink | flex-basis */
flex: 1 1 150px;
}
</style>
</head>
<body>
<div class="parent">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
</div>
</body>
</html>
可以看到,Flex容器内部分成了2行,但是Flex是基于行进行分配空间的,因此第2行两个子元素的宽度各占据了一半。
下图是第4个子元素的宽度
观察下面的Grid布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 800px;
height: 400px;
/* 定义当前元素使用grid布局系统 */
display: grid;
grid-template-columns: repeat(3, 1fr);
row-gap: 20px;
}
.child {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
<div class="child">
child1
</div>
<div class="child">
child2
</div>
<div class="child">
child3
</div>
<div class="child">
child4
</div>
<div class="child">
child5
</div>
</div>
</body>
</html>
可以看到,第2行的子元素的宽度可以与第1行的一样,同时可以很方便地设置行间距
row-gap
- 如果需要控制行或者列,选择Flex布局
- 如果需要控制行和列,选择Grid布局