界面布局 - Grid
前面介绍的 flexbox
布局是1维的, 本章介绍的 grid 网格布局
是2维的
基本概念
下面是一个 grid 布局 的示例 HTML
<head>
<style>
.wrapper {
display: grid;
gap: 1.8em 1.3em;
grid-template-columns: 100px 200px 300px;
}
.wrapper > div {
border: 1px solid teal;
}
</style>
</head>
<body style="padding:1em">
<div class="wrapper">
<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>
看起来,有点像表格table
但是table是用来放表格数据的,而不是用来做界面布局的。
grid 是专门用来做界面布局,摆放 html元素的。
div.wrapper
的 display
值为 grid
,指定了
-
内部显示规则是 grid 布局
-
外部显示规则是 block
-
该元素本身是一个 容器
grid容器 (container) 是整个grid布局的最外层包含元素
它的边界可以想象成 grid 的边框。
-
该元素的直接子元素是一个个 网格子项
grid item (子项) 是 container 的 直接子元素,
它们 占据grid里面的 一个或者多个
网格(cell)
一个网格通常具有许多的
列(column)
与行(row)
行与行、列与列之间的间隙,一般被称为
沟槽(gutter)
。grid 的 纵线或者横线 之间的条状空间 被称之为
轨道(track)
列布局
grid 布局中 grid-template-columns
指定了 列 的数量 和 宽度
<style>
.wrapper {
display: grid;
gap: 1.8em 1.3em;
grid-template-columns: 100px 200px 300px;
}
.wrapper > div {
border: 1px solid teal;
}
</style>
其中 gap: 1.8em 1.3em
是 row-gap
和 column-gap
的缩写
{
row-gap: 1.8em;
column-gap: 1.3em;
}
表明 行间距是 1.8em
, 列间距是 1.3em
如果 row-gap 和 column-gap 一样,比如都是 1em,可以更简单的写为
{
gap: 1em;
}
我们重点来看 grid-template-columns
,
{
grid-template-columns: 200px 300px 400px;
}
它指定了 网格有3列,从左到右,每列的宽度分别是 200px、 300px、 400px
wrap里面的5个div 子项元素,各占一个网格
第4个子项 超过了总列数3,就占据了第2行第1列这个网格。
如果你想指定每列的宽度占container宽的比例,而不是指定网格的固定宽度
可以这样
{
grid-template-columns: 1fr 2fr 3fr;
}
它指定了 网格有3列,从左到右,每列的宽度分别是 container宽的 1/6、 1/3 (2/6)、 1/2 (3/6)
指定比例的好处是:网格的列宽可以随着 container 宽度的变化而动态的变化,始终占满 container宽度
也可以使用百分比,表示这列占 container 的宽的 百分比
{
grid-template-columns: 20% 30% 40%;
}
也可以 固定宽度 和 比例宽度 混合使用
{
grid-template-columns: 200px 1fr 2fr;
}
它指定了 网格有3列,从左到右,第1列的宽度固定为 200px, 剩余2列的宽度分别是 container除了第1列剩余宽度的 1/3、 2/3
如果 有连续的宽度描述相同,可以使用 repeat()
标记
比如
{
/* 等价于 200px 200px 200px */
grid-template-columns: repeat(3, 200px);
/* 等价于 1fr 1fr 1fr 1fr */
grid-template-columns: repeat(4, 1fr);
/* 等价于 20px 1fr 1fr 1fr 1fr */
grid-template-columns: 20px repeat(4, 1fr);
/* 等价于 1fr 2fr 1fr 2fr 1fr 2fr */
grid-template-columns: repeat(3, 1fr 2fr);
}
使用非固定宽度,比如 1fr 1fr 1fr
,如果cell里面的内容超过了当前cell的设定宽度,cell会自动变宽以适应内容的宽度。
如下
<head>
<style>
.wrapper {
display: grid;
gap: .3em;
grid-template-columns: 1fr 1fr 1fr;
}
.wrapper > div {
border: 1px solid teal;
}
</style>
</head>
<body>
<div class="wrapper">
<div>1</div>
<div>2222222222222222222222222222222222222</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
</body>
而如果 指定固定宽度,比如 200px 200px 200px
, 如果cell里面的内容超过了当前cell的设定宽度,cell边界不会变化,cell里面的内容会跨越边界。
grid-template-columns 的值还可以是 auto
, 这种情况列宽主要是由 列中条目item元素的大小
决定
比如
<head>
<style>
.wrapper {
display: grid;
gap: .3em;
grid-template-columns: 1fr auto auto;
}
.wrapper > div {
border: 1px solid teal;
}
</style>
</head>
<body>
<div class="wrapper">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
可以发现 1fr auto auto
中 1fr 指定的第1列占据了几乎所有的宽度,
剩余 auto 指定的 第2、3列 只占据item元素的宽度,本例中就是1个字符的宽度。
但是,如果我们把 第2、3列 内容增多,比如改为
<div>222222222222</div>
<div>333333333333</div>
auto 指定的 第2、3列 占据的宽度就会相应的变大。
行布局
grid-template-rows
指定了 行 的数量 和 行高度
但是和 grid-template-columns
不同的是, 它并 不能限定
grid布局里面 总的行数
比如
<head>
<style>
.wrapper {
display: grid;
gap: .5em;
grid-template-rows: 100px 100px;
}
.wrapper > div {
border: 1px solid teal;
}
</style>
</head>
<body>
<div class="wrapper">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6<br>6<br>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
</div>
</body>
由于没有 grid-template-columns 指定列数,缺省值为none,结果就是只有1列。
这里 grid-template-rows: 100px 100px;
只能指定前2行的高度是 100px,
当元素超过 2 个 时,后续元素产生在新行中。 总数不受 grid-template-rows 指定 行数限制。
指定 grid-template-rows 时, 可以和 grid-template-columns 一起指定
比如
.wrapper {
display: grid;
gap: .5em;
grid-template-rows: 100px 100px;
grid-template-columns: 50px 50px;
}
grid-template-rows 还可以是其它的值
比如 : repeat
、 fr
、 百分比
、auto
这些值的含义和 grid-template-columns 类似, 不再赘述。
看这个例子
<head>
<style>
body{
height: 100vh;
margin: 0;
display: grid;
gap: 5px;
grid-template-rows: auto 1fr auto;
}
div {
border: 1px solid teal;
}
</style>
</head>
<body>
<div>导航栏</div>
<div>
正文
<pre>
2
2
2
2
2
2
2
</pre>
</div>
<div>页脚</div>
</body>
其中 grid-template-rows: auto 1fr auto
指定了3行内容,
导航栏 和 页脚 的值为 auto
,表示该行的高度 由其内容决定
而正文 值为 1fr
,并且没有其它fr,表示该行的高度占满除了 导航栏 和 页脚以外的全部高度
隐式指定track
前面我们这样用 grid-template-rows: 100px 100px
,是一种 显式指定
也就是明确地指定行 的数量 和对应的 track 的 高度和宽度 。
还有一种方式 叫 隐式指定(全局指定)
比如:可以使用 grid-auto-rows
隐式指定 track 的 高度
.wrapper {
display: grid;
gap: .8em .3em;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: 100px;
}
这样,不管产生的有多少行, 都是 grid-auto-rows 指定的高度 100px。
还可以这样写
{
/* row 的高度由其内容决定 */
grid-auto-rows: auto;
/* row 的高度是container的10% */
grid-auto-rows: 10%;
}
也可以是一组值 的 循环
{
grid-auto-rows: 50px 100px 150px;
grid-auto-rows: 0.5fr 3fr 1fr;
}
有时会这样写
{
grid-auto-rows: minmax(100px, auto);
}
表示 row 的高度由其内容决定,但至少是 100px
minmax 第1参数指定最小值, 第2个参数指定最大值。
规则是保证 min 一定满足的情况下,尽量取max值
元素跨行跨列
网格线
grid布局,网格是由 网格线
划分而成的
从左到右,列网格线编号依次从1开始,
从上到下,行网格线编号依次从1开始
如下图所示
通常我们的表格子项元素 是 依次一个个 分别放在一个个网格中的。
一个子项 放 一个 网格
但是也可以根据自己的需要,让某个子项占据多个网格位置
grid-column-start
, grid-column-end
, grid-row-start
, grid-row-end
这4个css属性分别指定 子项元素 占据 网格的起始线、终止线,从而指定了占据多少个网格
看下面的示例
<head>
<style>
.wrapper {
display: grid;
gap: .8em .3em;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 100px;
}
.box1 {
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 3;
}
.box2 {
grid-column-start: 1;
grid-row-start: 3;
grid-row-end: 5;
}
.wrapper > div {
border: 1px solid teal;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
<div class="box5">Five</div>
</div>
</body>
div.box1 被指定列网格线从1到4, 行网格线从1到3, 所以 占据了3列2行的空间。
结束边界线如果不指定,缺省为起始位置+1, 也就是占一格。
比如 div.box2 被指定列网格线从1到2, 行网格线从3到5, 所以 占据了1列2行的空间。
剩余的没有指定的 grid 条目元素,在不重叠的前提下, 尽量按照从上到下,从左到右 的次序 摆放。
简便写法
上面的
.box1 {
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 3;
}
.box2 {
grid-column-start: 1;
grid-row-start: 3;
grid-row-end: 5;
}
简便写法是
.box1 {
grid-column: 1 / 4;
grid-row: 1 / 3;
}
.box2 {
grid-column: 1;
grid-row: 3 / 5;
}
也可以使用 span
指定跨几个格子。
比如,上面的写法等价于
.box1 {
grid-column: 1 / span 3;
grid-row: 1 / 3;
}
.box2 {
grid-column: 1;
grid-row: 3 / span 2;
}
也可以使用负数指定网格线,表示反方向计数的网格线
.box1 {
grid-column: 1 / -1;
grid-row: 1 / 3;
}