07.grid布局
07.grid布局
7.1 grid布局基础
网格布局是由一系列水平和垂直结构构成的一种布局模式,创建在每行固定位置和有固定宽度的元素展示页面
一个网格通常具有许多的列(column)与行(row),以及行与行、列与列之间的间隙,这个间隙一般被称为沟槽(gutter)

grid布局
栅格容器从弹性容器沿袭了相当多的概念
- 栅格容器的子元素是栅格元素,就像弹性容器的子元素是弹性元素等
- 也有
grid和inline-grid两种方式 - 栅格容器没有
:first-line和:first-letter伪元素 - 浮动的元素不会打乱栅格,对栅格容器内元素使用
float无效 - 栅格的外边距不会重叠
vertical-align无效,应该使用grid布局内的对齐属性
基本的栅格术语
- 栅格容器是确立栅格的框体,其中的元素根据栅格布局规则排布,栅格比表格布局强大得多
- 栅格元素是栅格排布的元素,通常是子元素,也可能是匿名元素
- 栅格线是最重要的组件,其他栅格组件随着栅格线的定义出现
- 栅格轨道(
grid track):两条相邻的栅格线夹住的整个区域 - 栅格单元(
grid cell):四条栅格线限定的区域 - 栅格区域(
grid area):四条任意的栅格线限定的矩形区域,由多个栅格单元构成

栅格容器内术语 栅格轨道、栅格单元和栅格区域都完全由栅格线建构,不一定非要有相应的栅格元素存在
栅格区域中不一定充满栅格元素,完全可以让部分甚至多数栅格单元空着。此外,栅格元素还可以重叠,方法是定义重叠的栅格区域,或者把栅格线重叠起来
另外要注意的一点是,栅格线的数量不限,想定义多少就可以定义多少。你可以只定义一系列纵向的栅格线,创建一行多列布局。你也可以反过来,创建多个行轨道但不创建列轨道(当然还是会有一个列轨道,从栅格容器的一边延伸到对边)。然而,如果栅格元素无法放入你定义的列或行轨道中,或者你明确指定把栅格元素放在轨道的外部,那么栅格系统将自动添加栅格线和轨道
7.2 栅格线
栅格线如此重要,定义和放置方式多种多样,不同的放置方式有细微差别
大致定义栅格线是通过
grid-template-rows和grid-template-columns属性完成的,分别定义行栅格线间隔和列栅格线间隔,两个属性的可用值是相同的,分放置方式,有不同的写法/* 常规写法,提供相邻栅格线间隔 */ grid-template-columns: 1fr 1fr 1fr; /* 还可以在中间添加[栅格线名称]命名 */ /* 可以重名,方便给重名的线设置相同的属性 */ /* 行和列不共用命名空间 */ grid-template-columns: [start] 1fr [middle] 1fr [end];宽度固定的栅格轨道:宽度固定指的不是使用固定长度,而是宽度不随内容变化,比如
grid-template-rows: 200px 50% 100px;宽度计算
栅格宽度如果需要精细计算,可以使用函数辅助
calc():比如三列可以通过减法,减去两边的宽度,使中间宽度占满剩余空间grid-template-rows: [start masthead] 3em [content] calc(100%-5em) [footer] 2em [stop end];minmax():定义了一个长宽闭区间,包含两个参数,(最大值,最小值)- 让结果不大于最大值,也不小于最小值,浏览器将计算具体值
- 如果
最大值<最小值,则最大值被忽略并且minmax(最小值, 最大值)被看成最小值 - 两个参数都可以设置为
max-content,表示网格的轨道长度自适应内容最大的那个单元格,尽可能全放下 - 两个参数都可以设置为
min-content,表示网格的轨道长度自适应内容最小的那个单元格 - 两个参数都可以设置为
auto,作为最大值时,等价于max-content,作为最小值时,它表示轨道中单元格min-width和min-height中的最大值
grid-template-rows: [start masthead] 3em [content] minmax(3em,100%) [footer] 2em [stop end];
设置的间隔超过行或列宽度的处理:当设置值超过宽度时,将发生溢出确保布局所有栅格线
弹性宽度的栅格轨道:与宽度固定的栅格轨道相比,弹性栅格轨道的尺寸基于弹性容器中非弹性轨道以外的空间确定,或者基于整个轨道中的具体内容而定
在属性值部分介绍过
fr,可以将栅格容器分割成一定份数,比如如果希望得到5条宽度相同的栅格轨道,并希望之后方便增加轨道/* 之后只需要再添加新的fr就可以新增轨道 */ grid-template-rows: 1fr 1fr 1fr 1fr 1fr;fr不止可以作为百分比使用,还有更强大的功能,如果使用了固定值,fr只会百分比分配剩余空间,不需要额外calc函数计算,fr也可以使用非整数值- 如果没有剩余空间,对应轨道空间将为0
- 可以使用
minmax函数中的max-content和min-content,设定弹性宽度 fit-content函数可以对max-content宽度做出限制,接受一个长度或百分比参数,fit-content(argument)=>min(max-content, max(min-content, argument))- 当内容宽时,
max-content > argument,相当于设置了最大宽度 - 当内容窄时,
max-content < argument,相当于minmax(min-content, max-content)
- 当内容宽时,
重复栅格线:如果需要创建的各栅格轨道的尺寸是有重复规律的,可以使用
repeat函数,就不用重复输入了,repeat无法嵌套使用/* 重复构建1fr宽度的轨道5次 */ grid-template-rows: repeat(5, 1fr); /* 重复构建1fr宽度的轨道、2fr宽度的轨道共3次 */ grid-template-rows: repeat(3, 1fr 2fr); /* 可以命名栅格线,也可以在前后添加额外不重复的值 */ /* 其中相邻线的命名会被合并,也就是为[start end] */ grid-template-rows: 10px repeat(3, [start] 1fr [col-middle] 2fr [end]) 30px; /* 可以使用min-content和max-content作为长度值 */ grid-template-rows: repeat(4, [col-start] min-content [col-middle] max-content [col-end]);- 自动填充轨道:如果希望自适应重复次数,可以使用
auto-fill或auto-fit作为重复次数auto-fill如果网格容器在相关轴上具有确定的大小或最大大小,则重复次数是最大可能的正整数,不会导致网格溢出其网格容器auto-fit行为与auto-fill相同,但放置网格项目后,所有空的重复轨道都将折叠
- 自动填充轨道:如果希望自适应重复次数,可以使用
7.3 栅格区域
画好栅格线以后,可以使用
grid-template-areas创建栅格区域,属性接收一个字符串参数,一般仅用于命名或预览栅格- 参数使用一个字符串或多个拼接字符串表示,字符串应该包含多个被空格隔开的栅格单元名称字符,名称可以随意选取
- 字符串中的栅格单元名称数应该与划分的栅格单元数相同,否则属性无效
- 相同的栅格单元名称将合并在一起,要求使用相同的名称的单元相邻,否则属性无效
- 使用时,子元素通过
grid-area对应栅格单元名称 - 如果希望中间有不使用的栅格单元,可以使用
.代替名称,一个或多个相连的.效果也是一样的
使用grid-template-areas预览栅格区域<section id="page-content"> <header>Header</header> <nav>Navigation</nav> <main>Main area</main> <aside>aside</aside> <footer>Footer</footer> </section>#page-content { display: grid; width: 300px; height: 350px; grid-template-columns: repeat(3, 1fr); grid-template-rows: 50px 1fr 0.5fr 50px; grid-template-areas: "header header header" "left main right" "left main ..." "footer footer footer"; } #page-content > header { grid-area: header; /* 指定当前元素所在的区域位置, 从 grid-template-areas 选取值 */ background-color: #8ca0ff; } #page-content > nav { grid-area: left; background-color: #ffa08c; } #page-content > main { grid-area: main; background-color: #ffff64; } #page-content > aside { grid-area: right; background-color: #5f73f3; } #page-content > footer { grid-area: footer; background-color: #8cffa0; }之前的栅格线名称也可以定义栅格区域,不过可能必须指定四条线,如果定义的栅格线名称为
content-start和content-end,那么将在两线之间尝试创建名称为content的栅格区域,反过来说创建了栅格区域名称也就得到了栅格线名称,建议还是命名栅格区域名称,而不是定义栅格线名称简写属性
grid-template是grid-template-row、grid-template-column和grid-template-areas的结合- 如果只希望添加
row和column,使用时先行后列,在行属性和列属性中间用/隔开
#page-content { grid-template: 50px 1fr 0.5fr 50px / repeat(3, 1fr); }- 如果还需要添加
areas,将行列信息使用新语法拼接到区域属性中- 区域每行最后添加当前行的
grid-template-row属性 - 最后用
/隔开并添加grid-template-column的完整列表 - 完整语法中不允许使用
repeat,必须显式指定完整列表
- 区域每行最后添加当前行的
#page-content { grid-template: "header header header" 50px "left main right" 1fr "left main ..." 0.5fr "footer footer footer" 50px / 1fr 1fr 1fr; }- 如果只希望添加
7.4 添加栅格元素
- 定义好栅格区域后,就可以引用栅格线或栅格区域附加栅格元素
7.4.1 通过栅格线添加栅格元素
使用
grid-row-start和grid-row-end属性可以把栅格元素附加到行栅格线之间,同样的,使用grid-column-start和grid-column-end可以把栅格元素附加到列栅格线之间- 每个属性都可以接收一个数字,表示是第几根线,从1开始计数,与
python索引类似,接受负数 - 当然也可以接收一个栅格线名称,如果多条栅格线使用相同的名称,还需要跟随一个数字表示是第几条名称相同的栅格线,默认是第一条
- 可以接收
span关键字,span int_value,int_value表明从确定的一根栅格线向当前需要跨过的栅格线计数,实际上也对应了希望跨越多少个区域,值仅支持正数和-1- 如果定义了开始栅格线,把结束栅格线设为
span值,那么将向栅格结束的方向计数,一般情况下定义最后一根被预定义好的栅格线可以使用-1标识,不会因为有隐式栅格而变化 - 反过来,如果定义了结束栅格线,而开始栅格线是
span值,那么将向栅格开始的方向计数 - 可以再为
span提供一个栅格线名称,即span grid_line_name int_value,表示需要跨过对应数量的相同名称的线
- 如果定义了开始栅格线,把结束栅格线设为
- 默认值是
auto,对元素的放置没有影响,自动放置,一般是跨一个栅格线,如果start已经是最后一条线,end使用auto将占满所有栅格轨道
简写属性
可以使用
grid-row和grid-column合并上述属性,进行简写- 应该提供
<grid-line> [/<grid-line>]?值,左边的值提供给start,右边的值提供给end,当只提供一个栅格线指定值时,第二个值为auto - 如果提供的是区域名称,比如
footer,如果存在这样的区域名称,浏览器将尝试自动转换为footer-start/footer-end。在区域名称和线名称重名时,这个转换会导致线名称被忽略,因此最好将线名称和区域名称分开命名
示例<div id="grid-container"> <div id="grid-cell1">1</div> <div id="grid-cell2">2</div> <div id="grid-cell3">3</div> <div id="grid-cell4">4</div> </div>#grid-container { display: grid; width: 400px; height: 340px; grid-template-rows: repeat(4, 1fr); grid-template-columns: repeat(5, [col-A] 1fr [col-B] 1fr); background: linear-gradient(to right, #ffa08c 1px, transparent 1px), linear-gradient(to bottom, #ffa08c 1px, transparent 1px); background-size: 10% 25%; border-bottom: #ffa08c 1px solid; border-right: #ffa08c 1px solid; } #grid-container > * { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; border: rgba(66, 101, 153, 0.7) 1px solid; box-sizing: border-box; } /* 通过span指定另一条栅格线 */ #grid-cell1 { grid-row: 1 / span 2; grid-column: span 1/ 3; background-color: #7185; } #grid-cell2 { grid-column: col-A 3; grid-row: span 1; background-color: rgba(165, 42, 42, 0.3); } /* 通过span 栅格线名称 需要跨过的栅格线数量*/ /* 回退到前第2条col-A栅格线上 */ /* 由于这里col-A, col-B交替, 实际上是回退4格 */ #grid-cell3 { grid-column: span col-A 2/ 5; grid-row: span 2/ 5; background-color: #FFFF004D; } /* 支持负数索引 */ #grid-cell4 { grid-row: 3/ -1; grid-column: span col-A 2/ 11; background-color: #0080004D; }- 每个属性都可以接收一个数字,表示是第几根线,从1开始计数,与
7.4.2 隐式栅格和异常处理
- 隐式栅格:如果栅格元素或其一部分超过了显式定义的栅格,浏览器会再创建一些栅格线,并由此而诞生的新的轨道,这些都是隐式栅格的一部分
- 如果是通过栅格线名称的索引,隐式栅格线将带有对应的名称,也就是说,如果有一根栅格线没有办法出现这么多次,就额外创建一根,并设置上对应名字,一根隐式栅格线可能因此带有不同的名字
- 隐式栅格会选择能够将栅格元素内容恰好都放下的尺寸,当然也可以使用
grid-auto-rows和grid-auto-columns进行设置,都只接收一个栅格线创建时的长度值,包括fr和其他长度单位,max-content,min-content,auto,也可以使用minmax函数 - 最好不要出现隐式栅格线,应该调整模板值,确保所有的内容都能放下
对于隐式栅格,索引
-1不能定义到隐式分隔线
错误处理:有些异常情况需要处理,以防出现奇形怪状的栅格
- 如果不小心把开始线放在结束线后面:对调两个值
grid-row-start: 5; grid-row-end: 2; /* 得到的将是: */ grid-row-start: 2; grid-row-end: 5;- 如果开始线和结束线都声明为跨度:结束线的值将被丢弃,替换为
auto- 此时,栅格元素的结束边界根据当前栅格流自动放置,而开始边界则放在前一条栅格线上
grid-column-start: span; grid-column-end: span 3; /* 得到的将是: */ grid-column-start: span; grid-column-end: auto;- 如果只用具名跨度指明栅格元素的位置:具名栅格将被替换为1
grid-column-start: span footer; grid-column-end: auto; /* 得到的将是: */ grid-column-start: span; grid-column-end: auto;
7.4.3 通过栅格区域添加栅格元素
- 使用
grid-area可以简单地把元素指定给定义好的栅格区域,在之前栅格区域示例中,已经使用过这个属性了,但实际上,这个属性不止接收一个参数而是四个,依次对应上左下右四边(顺序与边距相反,这里是逆时针)的栅格线- 在传入四个参数时,对应四边栅格线,可以使用栅格区域名称,最后会分别对应开始和结束栅格线
- 如果提供的值少于四个,缺少的值根据提供的值确定
- 如果只有三个值,有两种情况,如果
column-start的值是栅格线的名称,那么缺少的column-end值与column-start的对应的栅格区域值相同;如果开始线的值是一个数字,那么结束线的值被设为auto - 如果只提供两个值,类似提供三个值时进行处理,当
row-start的值是栅格线的名称时,缺少的row-end值与之相同,否则设为auto - 如果提供一个值,如果是栅格区域名称,则四个值都使用栅格区域名称,如果是数字,剩下三个值都是
auto
- 如果只有三个值,有两种情况,如果
7.4.4 栅格流
在添加栅格元素时,是可能出现重叠的,哪个元素在上面取决于栅格的分层行为,可以通过
z-index和order控制,如果不显式使用grid-column等属性固定添加元素的位置,会在栅格流的作用下选择区域,确保元素不重叠栅格流可以通过
grid-auto-flow控制,支持的值如下row行优先:该关键字指定自动布局算法按照通过逐行填充来排列元素,在必要时增加新行,是默认值column列优先:该关键字指定自动布局算法通过逐列填充来排列元素,在必要时增加新列dense密集流(附加在行列优先之后):该关键字指定自动布局算法使用一种“稠密”堆积算法,如果后面出现了稍小的元素,则会试图去填充网格中前面留下的空白。这样做会填上稍大元素或由于设置了元素起始位置而留下的空白,但同时也可能导致原来出现的次序被打乱
示例<div id="grid-container"> <div id="item1">1</div> <div id="item2">2</div> <div id="item3">3</div> <div id="item4">4</div> <div id="item5">5</div> <div id="item6">6</div> <div id="item7">7</div> <div id="item8">8</div> <div id="item9">9</div> <div id="item10">10</div> <div id="item11">11</div> <div id="item12">12</div> <div id="item13">13</div> <div id="item14">14</div> </div> <select id="direction" onchange="changeGridAutoFlow()"> <option value="column">column</option> <option value="row">row</option> </select> <input id="dense" type="checkbox" onchange="changeGridAutoFlow()"/> <label for="dense">dense</label>function changeGridAutoFlow() { var grid = document.getElementById("grid-container"); var direction = document.getElementById("direction"); var dense = document.getElementById("dense"); var gridAutoFlow = direction.value === "row" ? "row" : "column"; if (dense.checked) { gridAutoFlow += " dense"; } grid.style.gridAutoFlow = gridAutoFlow; }#grid-container { height: 300px; width: 200px; display: grid; grid-gap: 10px; grid-template: repeat(6, 1fr) / repeat(4, 1fr); grid-auto-flow: column; /* or 'row', 'row dense', 'column dense' */ } #grid-container > * { background-color: #25724a; display: flex; justify-content: center; align-items: center; border: #25724a 1px solid; color: #252772; } #item1 { grid-row: 2; } #item2 { grid-column-end: span 2; } #item4 { grid-column: 3; } #item8 { grid-row-end: span 2; } #item11 { grid-row-end: span 2; grid-column-end: span 2; }实际情况下,由于显式定位(也就是有设置
grid-column或grid-row的元素)和自动定位的栅格元素并存--后者将围绕前者放置,情况要复杂得多
7.4.5 子栅格
子栅格:将
display:grid;添加到网格容器中时,只有直接子项成为网格项,有的时候需要通过将网格项作为网格容器来“嵌套”网格。但是,这些网格独立于父网格,并且彼此独立,这意味着它们不从父网格获取轨道大小。这使得嵌套的网格项很难与主网格对齐,因此出现了子栅格的概念- 子栅格用于使子网格容器的网格线和父容器对齐,可以通过
grid-template-rows: subgrid;和grid-template-columns: subgrid;分别设置子网格容器的行网格线和列网格线和父容器对齐 - 如果在父网格上指定了间距、列间距或行间距,则此间距将被传递到子网格中,因此子网格将与父网格具有相同的轨迹间距。但是,在某些情况下,您可能希望子栅格轨迹具有不同的间隙或没有间隙。这可以通过在子网格的网格容器上使用
gap-*属性来实现,将间隙空间平分,如下图中的棕色块的底部行间隙

间隙平分效果 - 如果需要命名子网格中的栅格线可以在
subgrid值之后添加每条栅格线的名称,比如grid-template-rows: subgrid [sub-a] [sub-b] [sub-c] [sub-d] [sub-e] [sub-f];
目前的支持有限,2023年才完成主流浏览器适配
- 子栅格用于使子网格容器的网格线和父容器对齐,可以通过
7.4.6 grid属性
grid属性是显式网格属性grid-template-rowsgrid-template-columns和grid-template-areas,隐式网格属性grid-auto-rows、grid-auto-columns和grid-auto-flow,间距属性grid-column-gap和grid-row-gap的简写,但实际上每次只能选择一个类型的属性- 显示网格属性:将
grid-template-*的属性重新整合在一起,更直观,不过这样列线间距就不能通过repeat定义了
grid: "header header header" 50px "left main right" 1fr "left main ..." 1fr "footer footer footer" 50px/ repeat(3, 1fr);- 隐式网格属性:将栅格流属性结合到隐式栅格属性中,并将栅格流属性取值修改为
auto-flow和额外的可选附加属性dense- 如果
auto-flow出现在隐式行栅格宽度grid-auto-rows中,如下一,相当于行优先row - 如果
auto-flow出现在隐式列栅格宽度grid-auto-columns中,如下二,相当于列优先columns - 添加
dense表示使用auto-flow中的密集流
- 如果
grid: auto-flow dense 2em/ minmax(20rem, max-content); grid: 2em/ auto-flow minmax(20rem, max-content);- 子栅格
subgrid:允许子元素的网格线与父元素的网格线对齐,这意味着子元素可以跨越父元素的多列或多行,而不需要显式地定义子元素的网格线,相当于grid-template-columns: subgrid;grid-template-rows: subgrid;
- 显示网格属性:将
7.4.7 栅格的其他属性
- 此部分包含栅格容器的栏距、其他属性对栅格的影响、栅格对齐方式、栅格元素的分层方式
- 栏距:如果希望栅格轨道不挤在一起,需要增加栏距,简单来说,栏距就是两个栅格轨道间的间隔,好似把栅格线加粗
- 一个方向只能设置一种栏距,也就是
row-gap和column-gap,都接收一个绝对长度值或百分比,当然也可以合并在一起,使用gap代替 - 如果
gap只提供一个值,那么row-gap和column-gap将使用相同值 - 双值属性应该先行
row-gap后列column-gap
早期定义的是
grid-gap,现代浏览器为了兼容也接受grid-gap作为别名 - 一个方向只能设置一种栏距,也就是
- 外边距对栅格的影响:如果希望有不同的栏距,可能需要使用盒子模型的外边距实现,如果使用了外边距,相对于栅格区域四周栅格线的距离将不是0,表现出来就是栅格元素收缩或溢出扩张(使用负外边距)
- 计算
min-content时将忽略元素的外边距 - 计算
fr时也不会受到外边距的影响 - 相当于只改变了当前栅格元素自身的大小
- 计算
- 绝对定位对栅格的影响:当然,不同边距也可以使用绝对定位实现,当然这要求栅格容器不是
static定位的,如果一个绝对定位栅格元素添加到非static定位栅格容器中的栅格区域之后,那绝对定位的属性将在栅格区域的格式化上下文中处理,包括偏移、边距和尺寸等absolute会根据最近的祖先非static元素上下文定位,如果栅格容器不使用其他定位方式,将无法根据栅格容器绝对定位- 如果提供分别两根
grid-row和grid-column结束和开始栅格线的位置,将根据栅格区域四边定位 - 如果不提供
grid-row或grid-column结束和开始栅格线的位置,默认将使用auto,元素使用auto的对应方向将相对于栅格容器定位
栅格对齐方式:默认未设置宽高情况下,栅格元素将占满栅格区域,但如果设置了具体的宽高,栅格元素使用和flex一致的属性,并额外支持
justify-items属性,可使用属性和用途列表*-content用于对齐栅格容器中的栅格区域,在所有栅格区域空间的大小比容器大小小时,栅格区域之间的分布方式将通过此属性调整*-items用于设置栅格区域中元素的对齐方式*-self用于对一个元素单独设置对齐方式
具体取值见
flex布局部分,取值和效果几乎完全一样
对齐属性和用途列表 栅格的分层方式:栅格元素可能出现重叠,所以需要定义分层方式,重叠顺序默认是后出现的覆盖新出现的,将通过
z-index或order指定- 这两个属性效果是一样的,数字越大离用户越近,放在上面
order就是修改了元素的渲染顺序,因此元素的位置可能也会发生变化,因此如果元素位置是auto应该调整元素的代码位置顺序
