说明
在CSS
当中,布局一直是一个非常重要的话题,在漫长的发展时间当中,出现了很多种布局方案,但是就整体来说主要分成解决PC端
和移动端
布局的两大类,在这两大类中包括例如传统的table
布局,后期的display+float+position
的布局方案,再比如CSS3
中新增加的Flex
布局和多列布局
等等,而在本篇博文中,将会主要讲述一种比较新的布局方案Grid
布局。
正是因为Grid
布局的年纪较小,所以从兼容性上来说,要逊色于Flex
布局,但是从实际的开发效率上来讲,却是比Flex
更加强大,这也是本篇博文的出发点之一。
Grid布局
从某种意义上来说和Flex布局
很类似,都有着自己的一套属性和布局逻辑。但是从复杂度来讲,是要超过Flex布局
的,体现最直观的就是Grid
当中包含的属性,数量上远远超过Flex
布局。
所以如果你是一个新人,连Flex
都不是很熟练,那么建议你先去研究一下Flex
布局方案。你可以把Flex
理解为一维布局,而把Grid
理解为二维布局,如果你对Flex
非常熟练,将会让你对Grid
的学习更加的顺畅。
因为Grid
属性较多,你在学习Grid布局
之前一定要做好十足的心里准备。
基本概念
Grid布局
某种意义上来说,和table
是非常类似的,二者都是在网页中构建一个表格,然后将内容放进表格中从而实现布局的目的。
但是作为布局界新人的Grid
确是远远比table
更加的强大。
首先,先来弄清楚在Grid
当中非常重要的一些概念。
- 容器: 采用网格布局的区域称之为容器。
- 项目: 容器内部的子元素为项目。
- 行和列: 既然要在网页中构建表格,那么必然存在行和列。
- 单元格: 行和列的交叉区域,称为单元格。
- 网格线: 将容器划分成不同区域的线,称之为网格线。
如图:
开启Grid
如何将一个容器变成Grid
容器呢? 可以通过下面的属性来设置:
display:grid | inline-grid;
display:grid
可以将容器变为一个块级容器,容器内部采用网格布局,display:inline-grid
可以将容器变为一个行内块容器,容器内部采用网格布局。
行和列的划分
在容器的身上开启了网格布局之后,就可以来规划行和列。主要应用到下面的两个属性:
grid-template-rows
: 定义每一行的行高。grid-template-columns
: 定义每一列的列宽。
例如下面的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
300px;
height: 300px;
border: 1px solid #222;
/* 开启网格布局 */
display: grid;
/* 三行三列:每行高度100px 每列宽度100px */
grid-template-rows: 100px 100px 100px;
grid-template-columns: 100px 100px 100px;
}
</style>
</head>
<body>
<article></article>
</body>
</html>
实现的效果如下:
可以通过浏览器的辅助工具查看网格内部的具体划分,下面以火狐浏览器开发版为例:
需要注意的是,单元格的大小取决于行高和列宽,并不取决于单元格内部是否拥有子元素。例如上面的代码中,单元格内部并没有子元素,单元格也并没有受到任何影响。
行高和列宽可以使用绝对单位
px
,也可以使用相对的百分比
。
repeat() 函数
在设置行高和列宽的时候,如果多个值重复的定义不免有些麻烦,所幸Grid
提供了repeat()
函数,通过repeat()
函数可以实现批量的设置行高和列宽。例如上面的三行三列宽高各100px的网格,就可以通过下面的方式来设置:
article {
300px;
height: 300px;
border: 1px solid #222;
/* 开启网格布局 */
display: grid;
/* 三行三列:每行高度100px 每列宽度100px */
grid-template-rows: repeat(3, 100px); /* 第一个值表示重复的次数,例如三行就写3次 */
grid-template-columns: repeat(3, 100px);
}
在使用repeat()
函数时,也可以按照一定的规律重复,例如:
article {
...
grid-template-rows: repeat(2, 60px 100px)
...
}
上面的代码看上去设置的是2行,但是却被repeat
函数的第二个参数所影响,第二个参数中,设置了第一行的行高是60px
,第二行的行高是100px
,这种情况再被repeat
函数重复两次,最后就在网格种出现了四行,行高分别是60px
、100px
、60px
、100px
。
auto-fill
在设置行高或者列宽的时候,行数或者列数并没有固定,就可以使用auto-fill
来自动进行填充。
例如:
article {
400px;
height: 300px;
border: 1px solid #222;
/* 开启网格布局 */
display: grid;
/* 每行行高为100px,设置自动填充, 最终的行数为高度300px / 100px = 3 */
grid-template-rows: repeat(auto-fill, 100px);
/* 每列的列宽为100px, 设置自动填充,最终的列数为宽度400px / 100px = 4 */
grid-template-columns: repeat(auto-fill, 100px);
}
效果如下:
fr
在规划行高或者列宽的时候,可以通过fr
这个单位来实现按照比例划分。
例如:
article {
...
grid-template-rows: 1fr 1fr 1fr;
/* 等同于 */
grid-template-rows: repeat(3, 1fr)
...
}
上面的代码意思是一共划出三行,每行的高度 = 总高度 / 分数(也就是1fr + 1fr + 1fr = 3) , 这样做的好处是可以让行高或者列宽通过fr进行动态的变化。
gap
可以通过gap属性来设置行间距和列间距,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
320px;
height: 320px;
border: 1px solid red;
/* 开启grid */
display: grid;
/* 设置行高和列宽 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
/* 设置行间距和列间距 */
gap: 10px;
/*gap: 10px 5px; 设置两个值的时候,第一个值是行间距,第二个值表示列间距*/
}
.content div {
background-color: orange;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
</body>
</html>
效果如下:
根据网格线编号移动单元格内的元素
每个单元格的位置是可以指定的,具体的方法就是指定单元格的四个边框,分别定位在哪根网格线。
先来看代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid */
display: grid;
/* 设置行高和列宽 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
}
.content div {
background-color: orange;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
效果如下:
代码的效果如上,现在假如想要将数字1所在的单元格移动到红色区域的位置。就可以将单元格起始位置的行和列,结束位置的行和列所在的网格线调整为和红色区域相同即可。
设置网格线所在的位置可以通过下面的属性来进行设置:
grid-row-start
: 设置起始位置的行所在的网格线编号grid-column-start
: 设置起始位置的列所在的网格线编号grid-row-end
: 设置结束位置的行所在的网格线编号grid-column-start
: 设置结束位置的列所在的网格线编号
例如上面的红色区域所在的行起始位置的网格线为第三根网格线,那么grid-row-start
的值就为3,结束位置的网格线为第四根网格线,那么grid-row-end
的值就为4。
完整的css代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid */
display: grid;
/* 设置行高和列宽 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
}
.content div {
background-color: orange;
}
/* 通过调整开始和结束的行和列网格线所在的位置来设置第一个元素的位置移动 */
.content div:first-child {
grid-row-start: 3;
grid-row-end: 4;
grid-column-start: 2;
grid-column-end: 3;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
所实现的效果如下:
可以发现,第一个元素已经顺利的移动到了指定的位置。同时,需要注意的是,当第一个项目移动了之后,第二个项目立即占据了之前第一个项目的位置。
给每条网格线进行命名操作
在设置行高和列宽的时候,可以同时给每条网格线进行命名操作,命名之后可以方便后续的项目位置移动。
例如:
article {
...
grid-template-rows: [x1-start] 100px [x1-end x2-start] 100px [x2-end x3-start] 100px [x3-end];
grid-template-columns: [y1-start] 100px [y1-end y2-start] 100px [y2-end y3-start] 100px [y3-end];
...
}
通过网格线名进行项目位移
和上面说的位移一样,就是将网格线编号换成了网格线名。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid */
display: grid;
/* 设置行高和列宽 */
grid-template-rows: [x1-start] 100px [x1-end x2-start] 100px [x2-end x3-start] 100px [x3-end];
grid-template-columns: [y1-start] 100px [y1-end y2-start] 100px [y2-end y3-start] 100px [y3-end];
}
.content div {
background-color: orange;
}
/* 通过网格线名进行位移 */
.content div:first-child {
grid-row-start: x3-start;
grid-row-end: x3-end;
grid-column-start: y2-start;
grid-column-end: y2-end;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
如果在设置行高和列宽的时候,是通过repeat()
函数来完成,那么就可以通过下面的写法来对每条网格线进行命名。
article {
...
grid-template-rows: repeat(3, [r-start] 100px [r-end]);
grid-template-columns: repeat(3, [c-start] 100px [c-end]);
...
}
项目位移时的用法如下:
div {
...
grid-row-start: r-start 1;
grid-column-start: c-start 1;
grid-row-end: span 1;
grid-column-end: span 3;
...
}
简写属性: grid-row 和 grid-column
在设置项目的位置的时候,可以通过grid-row
和grid-column
这两个简写属性。
grid-row
属性的第一个值表示的是grid-row-start
,第二个值表示grid-row-end
,两个值之间用/
来分隔。
grid-column
属性第一个值表示grid-column-start
,第二个值表示grid-column-end
,两个值之间用/
来分隔。
如下:
div {
...
grid-row: 1 / 2;
grid-column: 1 / 4;
...
}
当然,在grid-row
和grid-column
两个属性种也可以写网格线的名字。
div {
...
grid-row: 1 / x3-end;
grid-column: 1 / y2-end;
...
}
效果是一样的。
span关键字
通过span
关键字,可以让项目跨域指定的单元格。
示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid</title>
<style>
article {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid */
display: grid;
/* 设置行高和列宽 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
}
article div {
background-color: orange;
}
/* 让第一个div占据第一行 */
article div:first-child {
grid-row: 1 / span 1;
grid-column: 1 / span 3;
}
article div:last-child {
grid-row: 2 / span 2;
grid-column: 2 / span 1;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
</article>
</body>
</html>
效果如下:
grid-area
通过grid-area
属性能够给项目设置具体的单元格位置。
第一个值表示开始的行 第二个值表示开始的列 第三个值表示结束的行 第四个值表示结束的列。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
300px;
height: 300px;
border: 1px solid #222;
/* 开启grid布局 */
display: grid;
/* 画出栅格线, 并且在画出栅格线的同时,给栅格线起一个名字,行的栅格线统称为r,列的栅格线统称为c */
grid-template-rows: repeat(3, [r] 100px); /* 表示行的第一根网格线就叫做r 1,第二个就是r 2 , 其他的以此类推*/
grid-template-columns: repeat(3,[c] 100px);
}
article div {
background-color: orange;
}
/* 通过grid-area 来给每一个单元格设置区域,第一个值表示开始的行 第二个值表示开始的列 第三个值表示结束的行 第四个值表示结束的列 */
article div:first-child {
grid-area: r 2/ c 1 / r 3 / c 4;
}
article div:last-child {
grid-area: r 1/c 2 / r 2 / c 3;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
</article>
</body>
</html>
效果如下:
给网格不同区域进行命名
在使用grid
进行布局的时候,可以通过grid-template-areas
来给不同的区域进行命名。
先来通过grid
布局画出一个三行两列的布局,模拟移动端网页开发种经常出现的一种布局结构:
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid</title>
<style>
* {
margin: 0;
padding: 0;
}
/* 模拟移动端的小屏幕 */
.content {
100vw;
height: 100vh;
/* 开启grid */
display: grid;
/* 三行两列 */
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 60px 1fr;
}
</style>
</head>
<body>
<div class="content">
</div>
</body>
</html>
效果如下:
现在如果需要将第一行的两个部分合并,也同时将第三行的两个部分该怎么办呢?
这个时候可以通过grid-template-areas
属性来对不同的区域进行命名。
如下:
.content {
100vw;
height: 100vh;
/* 开启grid */
display: grid;
/* 三行两列 */
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 60px 1fr;
grid-template-areas: "header header"
"nav main"
"footer footer";
}
现在假设有一个子元素想要占据第一行的全部内容,可以通过grid-area
属性来进行设置,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid</title>
<style>
* {
margin: 0;
padding: 0;
}
/* 模拟移动端的小屏幕 */
.content {
100vw;
height: 100vh;
/* 开启grid */
display: grid;
/* 三行两列 */
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 60px 1fr;
grid-template-areas: "header header"
"nav main"
"footer footer";
}
header {
/* 因为第一行的两个部分都是header区域,所以此时直接全部占领 */
grid-area: header;
background-color: #222;
}
nav {
grid-area: nav;
background-color: lightblue;
}
main {
grid-area: main;
background-color: lightblue;
}
footer {
grid-area: footer;
background-color: #222;
}
</style>
</head>
<body>
<div class="content">
<header></header>
<nav></nav>
<main></main>
<footer></footer>
</div>
</body>
</html>
效果如下:
上面的这种结构是非常常见的,例如:
在通过
grid-template-areas
属性对不同区域进行命名的时候,如果某些区域不需要命名,可以通过.
点来占位。
grid-auto-flow 调整排列方式
一般来说,默认的排列方式是从左向右,从上到下,可以通过grid-auto-flow
属性来设置项目到底是水平排列还是垂直排列。
如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid布局 */
display: grid;
/* 画出栅格线 */
grid-template-rows: repeat(3,100px);
grid-template-columns: repeat(3, 100px);
}
article div {
background-color: orange;
}
</style>
</head>
<body>
<!-- 栅格的流动方向 -->
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</article>
</body>
</html>
上面的代码种采用的是项目默认排列方式,等同于grid-auto-flow:row
的效果,浏览器中效果如下:
现在来添加grid-auto-flow
属性,让其垂直排列,如下:
article {
...
/* 改变网格排列方向,默认值是row,是行排列,column 则是列排列 */
grid-auto-flow: column;
}
效果如下:
grid-auto-flow
还有一种特殊的应用,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid布局 */
display: grid;
/* 画出栅格线 */
grid-template-rows: repeat(3,100px);
grid-template-columns: repeat(3, 100px);
/* 当存在剩余空间时,强制将剩余空间填满 */
grid-auto-flow: row;
}
article div {
background-color: orange;
}
article div:nth-child(1) {
grid-column: 1 / span 2;
}
article div:nth-child(2) {
grid-column: 2 / span 1;
}
</style>
</head>
<body>
<!-- 栅格的流动方向 -->
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</article>
</body>
</html>
浏览器效果如下:
如果想要从上到下的用元素填补剩余空间,可以如下:
article {
...
/* 当存在剩余空间时,强制将剩余空间填满 */
grid-auto-flow: row dense;
}
效果如下:
设置项目水平和垂直方向的排列位置
先来看下面的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid */
display: grid;
grid-template-columns: repeat(4, 60px);
grid-template-rows: repeat(3,60px);
}
article div {
background-color: pink;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</article>
</body>
</html>
在上面的代码中,创建了一个三行四列的表格,但是和之前不一样的是,在上面的代码中创建的网格无论是宽度和高度都是小于最外层容器的宽度和高度。
浏览器中的效果如下:
这个时候可以通过justify-content
属性来调整所有项目在容器中的水平位置,通过align-content
属性来调整所有项目在容器中垂直位置。
两个属性的属性值相同,如下:
start
end
center
space-around
space-between
space-evenly
如下,让所有的项目在水平方向居中,垂直方向在底部。
article {
...
justify-content: center;
align-content: end;
}
效果如下:
justify-content
属性和align-content
属性的简写属性为place-content
, place-content
第一个属性值表示align-content
的值,第二个属性值表示justify-content
的属性值。
例如上面的设置就可以写成:
place-content: center end;
设置项目在单元格中的位置
设置项目在单元格中的位置可以通过justify-items
属性来设置水平方向的位置,通过align-items
属性来设置元素在垂直方向上的位置。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
300px;
height: 300px;
border: 1px solid red;
/* 开启grid */
display: grid;
grid-template-columns: repeat(4, 60px);
grid-template-rows: repeat(3,60px);
/* 设置项目在单元格中垂直居中 */
justify-items: center;
align-items: center;
}
article div {
background-color: pink;
20px;
height: 20px;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</article>
</body>
</html>
效果如下:
justify-items
和align-items
的简写属性为place-items
,第一个值表示align-items
也就是垂直位置,第二个值表示justify-items
也就是水平位置。
设置某个项目在单元格中的位置
可以通过justify-self
属性来设置项目在单元格中的水平位置,通过align-self
属性设置项目在单元格中的垂直位置。
如下:
article div:first-child {
justify-self: end;
align-self: center;
}
效果如下:
简写属性为place-self
,第一个值表示垂直方向align-self
属性的值,第二个值表示水平方向justify-self
的位置。
属性总结
因为grid
属性较多,所以在这列了一个思维导图,如下:
想要查看具体可以点击这里查看。
总结
ok,到这,基本上grid
的内容就差不多了,虽然属性特别多,但是当你熟练掌握后,你会发现布局会异常简单。