1. 什么是 CSS Grid 布局?
想象一下你在报纸或杂志上看到的页面布局,内容被整齐地划分在行和列组成的网格中。CSS Grid Layout 就是一个用于网页的 二维布局系统,可以让你同时控制行和列的排列和对齐。它非常强大,尤其适合创建复杂的、非线性的页面布局。
核心概念:容器(Container)与项目(Item)
+-------------------------------------------+
| Grid Container (应用了 display: grid) |
| +-----------+ +-----------+ +-----------+ |
| | Grid Item | | Grid Item | | Grid Item | |
| | (子元素1) | | (子元素2) | | (子元素3) | |
| +-----------+ +-----------+ +-----------+ |
| +-----------+ +-----------+ |
| | Grid Item | | Grid Item | |
| | (子元素4) | | (子元素5) | |
| +-----------+ +-----------+ |
+-------------------------------------------+
- 网格容器 (Grid Container): 你应用
display: grid;或display: inline-grid;的 HTML 元素。它是所有网格项目的直接父元素。 - 网格项目 (Grid Item): 网格容器的直接子元素。它们会自动成为 grid 的一部分,并按照 grid 的规则进行排列。
2. 网格线 (Grid Lines)
网格线是构成网格结构的分界线,有水平和垂直两种。它们用于定位网格项目。网格线可以按数字(从 1 开始)或自定义名称来引用。
Col 1 Col 2 Col 3
+----------+----------+----------+ <--- Line 1 (水平/行线)
| Cell | Cell | Cell | Row 1
+----------+----------+----------+ <--- Line 2
| Cell | Cell | Cell | Row 2
+----------+----------+----------+ <--- Line 3
| Cell | Cell | Cell | Row 3
+----------+----------+----------+ <--- Line 4
^ ^ ^ ^
Line 1 Line 2 Line 3 Line 4
(垂直/列线)
- 一个 3x3 的网格会有 4 条水平网格线和 4 条垂直网格线。
3. 网格轨道 (Grid Tracks)
轨道是指两条相邻网格线之间的空间。
- 行轨道 (Row Track): 两条水平网格线之间的空间。
- 列轨道 (Column Track): 两条垂直网格线之间的空间。
.container {
display: grid;
/* 定义了 3 个列轨道: 第一个 100px 宽, 第二个 1fr 宽, 第三个 2fr 宽 */
grid-template-columns: 100px 1fr 2fr;
/* 定义了 2 个行轨道: 高度都自动 */
grid-template-rows: auto auto;
}
fr 单位:这是一个非常有用的弹性单位,代表网格容器中可用空间的一等份。1fr 2fr 意味着第二个轨道宽度是第一个轨道的两倍(在它们分配完非弹性空间之后)。
4. 网格单元 (Grid Cell)
网格单元是网格中的最小单位,由四条网格线包围形成的空间(一个行轨道和一个列轨道的交叉点)。
+----------+
| Cell | <-- 一个网格单元
+----------+
5. 网格区域 (Grid Area)
一个网格区域是由任意四条网格线定义的矩形空间,可以跨越一个或多个网格单元。你可以通过行/列的起始和结束线来定义一个区域,或者使用 grid-template-areas 属性来命名区域。
使用 grid-template-areas:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 3 列 */
grid-template-rows: auto 1fr auto; /* 3 行 */
grid-template-areas:
"header header header" /* 第一行都属于 header 区域 */
"sidebar main main" /* 第二行第一列是 sidebar,后两列是 main */
"footer footer footer"; /* 第三行都属于 footer 区域 */
}
.header-item {
grid-area: header; /* 将这个项目放到名为 header 的区域 */
}
.sidebar-item {
grid-area: sidebar;
}
.main-item {
grid-area: main;
}
.footer-item {
grid-area: footer;
}
“配图”说明: 上面的代码会创建如下布局结构:
+-----------------------------------+
| header |
+---------+-------------------------+
| sidebar | main |
| | |
+---------+-------------------------+
| footer |
+-----------------------------------+
HTML 代码示例:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Template Areas 示例</title>
<style>
/* 为了让效果更明显,添加一些基础样式 */
body { margin: 0; font-family: sans-serif; }
.container > * { /* 给所有 grid item 添加边框和内边距以便观察 */
border: 1px solid #ddd;
padding: 10px;
text-align: center;
}
/* --- 您提供的 CSS --- */
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 3 列 */
grid-template-rows: auto 1fr auto; /* 3 行: 页眉页脚自适应高度,主区域占满剩余空间 */
grid-template-areas:
"header header header" /* 第一行都属于 header 区域 */
"sidebar main main" /* 第二行第一列是 sidebar,后两列是 main */
"footer footer footer"; /* 第三行都属于 footer 区域 */
min-height: 100vh; /* 让容器至少撑满视口高度 */
gap: 5px; /* 添加一点间隙 */
}
.header-item {
grid-area: header; /* 将这个项目分配到名为 "header" 的网格区域 */
background-color: lightcoral; /* 添加背景色以区分 */
}
.sidebar-item {
grid-area: sidebar; /* 分配到 "sidebar" 区域 */
background-color: lightblue;
}
.main-item {
grid-area: main; /* 分配到 "main" 区域 */
background-color: lightgoldenrodyellow;
}
.footer-item {
grid-area: footer; /* 分配到 "footer" 区域 */
background-color: lightgreen;
}
/* --- 结束: 您提供的 CSS --- */
</style>
</head>
<body>
<div class="container">
<header class="header-item">页眉 (Header)</header>
<aside class="sidebar-item">侧边栏 (Sidebar)</aside>
<main class="main-item">主内容区 (Main Content)</main>
<footer class="footer-item">页脚 (Footer)</footer>
</div>
</body>
</html>
关键连接点解释:
- HTML 结构: 我们有一个父元素
<div class="container">,它是 Grid 容器。里面有四个子元素(这里用了语义化的<header>,<aside>,<main>,<footer>标签,用<div>也可以),它们是 Grid 项目。 - CSS 类名: 每个 Grid 项目都有一个特定的类名(
header-item,sidebar-item,main-item,footer-item)。 grid-template-areas: 在.container的 CSS 规则中,grid-template-areas属性用字符串定义了一个网格布局模板,并给模板中的不同区域命了名("header", "sidebar", "main", "footer")。grid-area: 在每个项目对应的 CSS 规则中(.header-item,.sidebar-item等),grid-area属性的值被设置为grid-template-areas中定义好的区域名称。- 连接: 浏览器看到
.header-item元素,找到对应的 CSS 规则发现grid-area: header;,然后就在.container的grid-template-areas定义中寻找所有标记为 "header" 的单元格,并将.header-item元素放置到那个区域。其他项目同理。
这样,即使 HTML 元素的顺序改变(比如你把 <footer> 写在最前面),只要 CSS 类名和 grid-area 分配正确,最终渲染出来的视觉布局仍然会是 grid-template-areas 所定义的那样。这就是 Grid 布局强大之处:布局结构可以独立于源码顺序。
6. 间距 (Gaps / Gutters)
你可以使用 gap (或旧的 grid-gap) 属性在网格轨道之间创建间距。
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
/* 行间距 10px, 列间距 20px */
gap: 10px 20px;
/* 或者只设置一个值,行列间距相同: gap: 15px; */
/* 旧语法: */
/* row-gap: 10px; */
/* column-gap: 20px; */
/* grid-gap: 10px 20px; */
}
“配图”说明: 这会在网格项目之间产生可见的空白缝隙。
+---------+ +---------+ <-- 20px 列间距
| Item 1 | | Item 2 |
+---------+ +---------+
^ 10px 行间距 v
+---------+ +---------+
| Item 3 | | Item 4 |
+---------+ +---------+
7. 放置项目 (Placing Items)
除了自动放置或使用 grid-template-areas,你还可以通过指定网格线来精确控制项目的位置和跨度。
grid-column-start/grid-column-endgrid-row-start/grid-row-end- 以及它们的简写属性
grid-column和grid-row。
.item-1 {
/* 从第 1 条列线开始,到第 3 条列线结束 (跨越 2 列) */
grid-column-start: 1;
grid-column-end: 3; /* 等同于 grid-column: 1 / 3; */
/* 从第 1 条行线开始,到第 2 条行线结束 (占据第 1 行) */
grid-row-start: 1;
grid-row-end: 2; /* 等同于 grid-row: 1 / 2; 或 grid-row: 1; */
}
.item-2 {
/* 从第 3 条列线开始,到第 4 条列线结束 */
grid-column: 3 / 4;
/* 从第 1 条行线开始,跨越 2 行,到第 3 条行线结束 */
grid-row: 1 / 3;
}
“配图”说明: 想象在之前的 3x3 网格中:
Col 1 Col 2 Col 3
+--------------------+----------+ <--- Line 1
| Item 1 | |
+--------------------| Item 2 | <--- Line 2
| (Implicit) | |
+--------------------+----------+ <--- Line 3
| (Implicit) | (Impl...)|
+--------------------+----------+ <--- Line 4
Item 1 占据了第 1 行的第 1、2 列单元格。Item 2 占据了第 1、2 行的第 3 列单元格。
8. 放置顺序(grid-auto-flow)
grid-auto-flow 属性控制着没有被显式指定位置的网格项(Grid Items)是如何被自动放置到网格容器(Grid Container)中的。它决定了自动布局算法的行为,特别是项目的流动方向以及是否尝试填充网格中的“空隙”。
想象一下你有很多物品(网格项),需要按顺序放进一个有很多格子的架子(网格容器)里。grid-auto-flow 就决定了你是一行一行地放,还是一列一列地放,以及如果某个物品特别大占了多个格子导致后面留下空位时,是否允许后面的小物品“插队”填补这个空位。
grid-auto-flow 的主要值
1. row (默认值)
这是 grid-auto-flow 的默认行为。网格项会优先水平填充每一行,一行填满了再换到下一行。
示例场景:
假设我们有一个 3 列的网格,里面有 5 个网格项。
HTML:
<div class="grid-container row-flow">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
CSS:
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 定义 3 列 */
grid-template-rows: repeat(2, 100px); /* 定义 2 行 (为了清晰) */
gap: 10px;
border: 2px solid lightcoral;
padding: 10px;
}
.row-flow {
/* grid-auto-flow: row; */ /* 这是默认值,可以不写 */
}
.item {
background-color: lightskyblue;
border: 1px solid blue;
display: flex;
justify-content: center;
align-items: center;
font-size: 2em;
}
效果:
你会看到一个 3 列 2 行的网格布局。
- 第 1 行: 放置了项目 1、项目 2、项目 3。
- 第 2 行: 因为第 1 行满了,项目 4 开始放在第 2 行的第 1 列,项目 5 放在第 2 行的第 2 列。第 2 行第 3 列是空的。
+-------+-------+-------+
| Item 1| Item 2| Item 3| <- 第一行被优先填满
+-------+-------+-------+
| Item 4| Item 5| | <- 换到第二行继续放
+-------+-------+-------+
2. column
当设置为 column 时,网格项会优先垂直填充每一列,一列填满了再换到下一列。
示例场景:
HTML:
<div class="grid-container column-flow">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
CSS:
/* ... (grid-container 和 item 的样式同上) ... */
.column-flow {
grid-auto-flow: column;
/* 为了让列有足够空间,可能需要显式定义更多行, */
/* 或者让行高自动适应,或者定义一个固定的列数 */
grid-template-rows: repeat(3, 80px); /* 假设我们定义了3行 */
grid-template-columns: repeat(3, 1fr); /* 仍然是3列 */
}
效果:
布局现在会按列填充。
- 第 1 列: 放置了项目 1、项目 2、项目 3 (假设我们定义了足够多的行,或者行高自动)。
- 第 2 列: 因为第 1 列满了 (或者达到了定义的行数),项目 4 开始放在第 2 列的第 1 行,项目 5 放在第 2 列的第 2 行。
- 第 3 列: 是空的。
+-------+-------+-------+
| Item 1| Item 4| | <- 第一列被优先填满
+-------+-------+-------+
| Item 2| Item 5| |
+-------+-------+-------+
| Item 3| | |
+-------+-------+-------+
(注意: 实际渲染时,如果没有足够的行定义,浏览器可能会隐式创建更多行或列来容纳内容,或者内容会溢出。)
3. dense 关键字
grid-auto-flow属性中的dense关键字,是一种可选的布局算法。它的主要目标是尝试填补网格中因为前面有项目跨越多行或多列而留下的空白间隙。浏览器会尝试将后面出现的、尺寸合适的项目“塞”进这些早先出现的空隙中,使得整个网格布局更加紧凑。
这样做的代价是,网格项在视觉上的顺序可能不再与其在 HTML (DOM) 中的顺序完全一致。
我们用一个清晰的例子来演示
这次,我们使用一个一开始就可能产生空隙的场景:让第一个项目(Item 1)占据2行。
1. 场景设置 (HTML 和基础 CSS)
HTML:
<div class="grid-container">
<div class="item item1">1 (占2行)</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item4">4</div>
<div class="item item5">5</div>
</div>
CSS:
.grid-container {
display: grid;
/* 定义 3 列,3 行 */
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 80px);
gap: 10px;
border: 2px solid lightcoral;
padding: 10px;
/* 我们先看默认的 row 流动 */
grid-auto-flow: row; /* 注意:没有 dense */
}
.item {
background-color: lightskyblue;
border: 1px solid blue;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.5em;
}
.item1 {
grid-row: span 2; /* 让第 1 个项目占据 2 行 */
background-color: lightcoral;
}
2. 效果:没有 dense (grid-auto-flow: row;)
浏览器会按照 HTML 顺序,一行一行地尝试放置项目:
- Item 1: 放在 R1C1 (Row 1, Col 1)。因为它
span 2行,所以它占据 R1C1 和 R2C1。 - Item 2: 放在 R1C2 (第 1 行的下一个可用位置)。
- Item 3: 放在 R1C3 (第 1 行的下一个可用位置)。 第 1 行满了。
- Item 4: 浏览器移动到下一行 (Row 2) 开始寻找位置。R2C1 被 Item 1 占了。根据标准的
row流动,它不会去检查 R2C2 或 R2C3 是否可用,而是完成当前行的检查后直接跳到下一行的起始位置。所以浏览器认为 Row 2 对于“新开始”的项来说没有合适位置(因为起始的 R2C1 被占),于是跳到 Row 3。Item 4 放在 R3C1。 - Item 5: 放在 R3C2。
文字图例 (没有 dense):
+----------+----------+----------+
| | | |
| Item 1 | Item 2 | Item 3 | <- Row 1
| (占 2 行)| | |
| | | |
+----------+----------+----------+
| | | |
| (Item 1) | | | <- Row 2 (C2, C3 留下了空隙)
| | | |
| | | |
+----------+----------+----------+
| | | |
| Item 4 | Item 5 | | <- Row 3
| | | |
| | | |
+----------+----------+----------+
Col 1 Col 2 Col 3
关键点: 注意第 2 行的第 2 列和第 3 列是空的。标准 row 流程不会让 Item 4 或 Item 5 回去填补这些空隙。
3. 效果:使用 dense (grid-auto-flow: row dense;)
现在,我们只修改一行 CSS:
CSS
.grid-container {
/* ... 其他样式不变 ... */
grid-auto-flow: row dense; /* 加上 dense 关键字! */
}
/* ... item 和 item1 样式不变 ... */
浏览器放置项目时的行为会有所不同:
-
Item 1: 放置方式不变,占据 R1C1 和 R2C1。
-
Item 2: 放置方式不变,占据 R1C2。
-
Item 3: 放置方式不变,占据 R1C3。
-
Item 4: 轮到 Item 4。因为是
dense模式,浏览器在放置它之前,会回头检查从网格开始位置起,是否有更早出现的、适合 Item 4 (1x1 大小) 的空隙。- 它检查 R1,满了。
- 它检查 R2。R2C1 被 Item 1 占用。R2C2 是空的! Item 4 尺寸合适。
- 于是,Item 4 被放置在 R2C2,填补了这个空隙。
-
Item 5: 轮到 Item 5。浏览器继续使用
dense算法寻找最早的可用空隙。- 它发现 R2C3 是空的! Item 5 尺寸合适。
- 于是,Item 5 被放置在 R2C3。
文字图例 (使用 row dense):
+----------+----------+----------+
| | | |
| Item 1 | Item 2 | Item 3 | <- Row 1
| (占 2行) | | |
| | | |
+----------+----------+----------+
| | | |
| (Item 1) | Item 4 | Item 5 | <- Row 2 (空隙被 Item 4, 5 填补了!)
| | | |
| | | |
+----------+----------+----------+
| | | |
| | | | <- Row 3 (现在是空的)
| | | |
| | | |
+----------+----------+----------+
Col 1 Col 2 Col 3
对比与总结
- 没有
dense:布局严格按照先行后列(或先列后行)的顺序流,遇到跨越多轨道的大项目时,可能会在它旁边留下空隙,后续项目不会回来填补。布局可能不紧凑。 - 使用
dense:布局算法会更“积极”地利用空间。当放置一个小项目时,如果前面有因为大项目而留下的、尺寸合适的空隙,浏览器会优先将这个小项目**“塞”回**那个空隙,使得最终布局更紧密。但这可能导致视觉顺序和 DOM 顺序不一致。
总结
grid-auto-flow: row;(默认): 按行优先填充,填满一行换下一行。grid-auto-flow: column;: 按列优先填充,填满一列换下一列。grid-auto-flow: row dense;: 按行优先填充,但允许后面的小项目填补前面留下的行内空隙。grid-auto-flow: column dense;: 按列优先填充,但允许后面的小项目填补前面留下的列内空隙。
dense 值可以创建更紧凑的布局,特别是在项目大小不一时,但代价是视觉顺序可能与 DOM 顺序不一致,这可能会影响可访问性。
9. 其他重要概念:
repeat()函数: 简化重复轨道定义的写法。grid-template-columns: repeat(3, 1fr);等同于1fr 1fr 1fr;。minmax(min, max)函数: 定义轨道的最小和最大尺寸。grid-template-columns: minmax(100px, 1fr);表示列宽至少 100px,但可以扩展占据更多可用空间。- 对齐 (Alignment): Grid 提供了丰富的属性来控制项目在其单元格内的对齐 (
justify-self,align-self) 以及所有项目作为一个整体在容器内的对齐 (justify-content,align-content)。
10. fr 单位是什么?
fr代表 "fraction unit"(分数单位) 。- 它是一个弹性长度单位,专门用于 CSS Grid 布局中。
1fr代表的是网格容器中可用空间 (available space) 的一份。
“可用空间”是指什么?
“可用空间”是指在网格容器的总尺寸中,减去以下部分后剩余的空间:
-
所有非弹性轨道 (non-flexible tracks) 所占据的空间。这包括:
- 使用固定单位(如
px,em)定义的轨道。 - 使用百分比 (
%) 定义的轨道(相对于容器尺寸)。 - 使用
auto、min-content、max-content等基于内容或固有尺寸 (intrinsic size) 确定的轨道。这些轨道的大小虽然由内容决定,但它们也属于优先计算尺寸、不参与剩余空间按比例分配的类型。
- 使用固定单位(如
-
所有轨道之间的间隙 (
gap) 。
fr 单位如何工作?
- 计算可用空间: 浏览器首先确定用于分配给
fr轨道的总可用空间是多少。 - 计算总
fr份数: 浏览器将所有定义为fr的轨道的系数加起来。例如,1fr 2fr 1fr的总份数是1 + 2 + 1 = 4份。 - 计算
1fr的大小: 将总可用空间除以总fr份数,得出1fr实际代表的尺寸(例如像素值)。 - 确定轨道尺寸: 每个
fr轨道的最终尺寸就是它的fr系数乘以计算出的1fr的大小。
以 grid-template-columns: 1fr 2fr; 为例解释
这行 CSS 定义了网格容器有两列,它们的宽度都是弹性的,并且它们的宽度比例是 1:2。
-
总
fr份数: 1 + 2 = 3 份。 -
空间分配:
- 浏览器计算出容器内水平方向的可用空间(容器宽度 - 左右
padding- 列间gap- 其他固定宽度列的宽度)。 - 将这个计算出的可用空间分成 3 等份。
- 第一列 (
1fr) 获得其中的 1 份。 - 第二列 (
2fr) 获得其中的 2 份。
- 浏览器计算出容器内水平方向的可用空间(容器宽度 - 左右
-
结果: 第二列的宽度将精确地是第一列宽度的两倍,并且这两列加起来(再加上它们之间的
gap,如果有的话)会正好填满它们可用的那部分容器空间。
简单代码示例:
<style>
.container {
display: grid;
/* 定义两列,宽度比例 1:2 */
grid-template-columns: 1fr 2fr;
gap: 10px;
width: 610px; /* 假设容器总宽 610px */
border: 1px solid blue;
}
.item {
background-color: lightcoral;
padding: 10px;
border: 1px solid red;
}
</style>
<div class="container">
<div class="item">第一列 (1fr)</div>
<div class="item">第二列 (2fr)</div>
</div>
-
计算过程:
- 容器宽度 610px。
- 间隙
gap= 10px。 - 可用于
fr分配的空间 = 610px - 10px = 600px。 - 总
fr份数 = 1 + 2 = 3。 1fr的大小 = 600px / 3 = 200px。- 第一列宽度 = 1 * 200px = 200px。
- 第二列宽度 = 2 * 200px = 400px。
- (验证:200px + 10px (gap) + 400px = 610px,正好填满容器)
-
计算与分配过程“图示”:
- 总览容器宽度:
[<------------------------- 610px ------------------------->] 容器 (Container)-
识别并“预留”非弹性空间 (Gap):
- 在
1fr和2fr这两列之间,有 1 个gap。 - 预留
gap空间:10px。
- 在
[<----------------- 可用于 fr 分配的空间 ----------------->][-- gap --] [<----------------------- ? px ----------------------->][--10px --] 总计 (Total): 610px-
计算可用于
fr分配的总空间:fr可用空间 = 容器总宽度 -gap总宽度fr可用空间 = 610px - 10px = 600px
[<------------------------- 600px ------------------------->][-- 10px --] (Space available for fr units) (Gap)-
计算总
fr份数:- 列定义是
1fr 2fr - 总份数 = 1 + 2 = 3份 (3fr)
- 列定义是
-
计算
1fr的具体大小:1fr大小 = (fr可用空间) / (总fr份数)1fr大小 = 600px / 3 = 200px
-
将可用空间按
fr比例划分:- 将 600px 的可用空间分成 3 份,每份 200px。
[--- 1fr (200px) ---][--- 1fr (200px) ---][--- 1fr (200px) ---] (这部分总和为 600px)-
分配给具体列:
- 第一列 (Column 1): 定义为
1fr,获得 1 份空间 = 200px。 - 第二列 (Column 2): 定义为
2fr,获得 2 份空间 = 2 * 200px = 400px。
- 第一列 (Column 1): 定义为
-
组合得到最终布局:
- 将计算出的列宽和
gap组合起来。
- 将计算出的列宽和
+----------------------+---------------+-------------------------------------+ | Column 1 | Gap | Column 2 | | (1fr) | | (2fr) | +----------------------+---------------+-------------------------------------+ <------- 200px --------><---- 10px ----><------------- 400px ----------------> <----------------------- 总宽度: 200 + 10 + 400 = 610px ---------------------->这个分步骤的示意图展示了:
- 如何先从总宽度中减去
gap得到fr可用的总空间 (600px)。 - 如何根据总
fr份数 (3fr) 计算出1fr的大小 (200px)。 - 如何将这个大小按比例分配给
1fr列 (200px) 和2fr列 (400px)。 - 最终各部分组合起来正好等于容器总宽度 (610px)。
总结:
fr 单位是 CSS Grid 中实现弹性布局和按比例分配空间的关键。1fr 2fr 这样的定义意味着将可用空间分成 3 份,第一个轨道占 1 份,第二个轨道占 2 份,使得它们的尺寸成 1:2 的比例。
11. auto-fill 和 auto-fit
使用场景和目标
auto-fill 和 auto-fit 关键字用于 grid-template-columns 或 grid-template-rows 的 repeat() 函数中,目的是让浏览器根据容器可用空间自动决定创建多少轨道(通常是列) 。它们常与 minmax() 结合,创建灵活且响应式的网格。
核心示例代码:
.container {
display: grid;
/* 使用 auto-fill 或 auto-fit */
grid-template-columns: repeat(auto-fill_or_auto-fit, minmax(100px, 1fr));
gap: 10px; /* 轨道间隙 */
}
这段代码设定每列宽度至少 100px,并且可以放大(1fr)以填充可用空间。
共同点: 都会先计算在容器中最多能放下多少个符合 minmax() 最小尺寸要求的轨道。
区别点: 对计算后多余的、未被项目填充的“空轨道”的处理方式不同。
1. auto-fill (自动填充)
-
行为: 创建尽可能多的轨道来填充容器,即使轨道是空的,也会保留它们并让它们参与空间计算。
-
核心机制:
- 计算最大潜力列数: 浏览器查看容器总宽度,并计入
gap。然后基于minmax(100px, 1fr)中的最小值100px,计算出在包含间隙的情况下,最多能放下多少列 (N)。 (例如,对于 730px 宽容器, gap 10px,N*100 + (N-1)*10 <= 730,最大 N=6)。 - 创建轨道:
auto-fill按照计算出的最大数量 (N=6) 创建列轨道。 - 计算
fr空间: 计算容器宽度减去所有实际产生的gap后的总可用空间 (730px - (6-1)*10px = 680px)。 - 分配
fr: 将可用空间 (680px) 平均分配给所有被创建的轨道 (N=6 个),因为它们都被定义为1fr。所以每个1fr≈ 113.33px。 - 确定最终宽度: 每列宽度取
max(100px, 113.33px)≈ 113px。
- 计算最大潜力列数: 浏览器查看容器总宽度,并计入
-
“图示” (3 个 Item,容器宽 730px,能放 6 列):
容器 (Container: 730px wide) | 使用 auto-fill | gap: 10px | track: minmax(100px, 1fr) 计算: 最多放 6 列, 1fr ≈ 113px, 每列宽 ≈ 113px 布局示意: +----------------------------------------------------------------------------------------------+ | +----------+ 10 +----------+ 10 +----------+ 10 +----------+ 10 +----------+ 10 +----------+ | | | Item 1 |--->| Item 2 |--->| Item 3 |--->| (空轨道4) |--->| (空轨道5) |--->| (空轨道6) | | | | (113px~) | | (113px~) | | (113px~) | | (113px~) | | (113px~) | | (113px~) | | | +----------+ +----------+ +----------+ +----------+ +----------+ +----------+ | +----------------------------------------------------------------------------------------------+ |<----------------- 3个项目 —---------------->|<------ 空轨道实际占据的空间 (导致右侧明显留白) ------>| -
结果说明: 只有 3 个项目,但
auto-fill创建了 6 个轨道。每个轨道宽度约为 113px。空的轨道 4、5、6 依然占据了它们计算出的宽度,导致项目 1、2、3 之后有大片空白区域,项目本身没有被拉伸很多。
2. auto-fit (自动适应)
-
行为:
auto-fit会计算容器最多能放下多少轨道,然后放置项目。如果发现有轨道是空的,它会将这些空轨道折叠掉(视为不存在) 。之后,剩余的、有内容的轨道会根据fr单位去瓜分容器的全部可用宽度(减去它们之间实际存在的gap)。 -
核心机制 :
-
计算最大潜力轨道数 (N) : 浏览器基于
minmax的最小值和gap,计算出理论上最多能放 N 列(例如 N=6)。这一步主要是为了判断是否有轨道会变空。 -
放置项目与识别空轨道: 将 3 个 Item 放入前 3 个轨道。识别出轨道 4, 5, 6 是空的。
-
折叠空轨道:
auto-fit机制触发,将空轨道 4, 5, 6 彻底视为不存在于最终的布局计算中。 -
计算
fr可用空间 (面向实际渲染) : 浏览器计算的是最终要渲染的轨道(即有内容的轨道)可以用来分配fr的空间。这个空间等于容器总宽度减去这些实际渲染轨道之间的gap总和。- 示例 (3 个内容项) : 实际渲染 3 列,它们之间有
3 - 1 = 2个gap。 - 示例:
fr可用空间 =730px - 2 * 10px= 710px。
- 示例 (3 个内容项) : 实际渲染 3 列,它们之间有
-
分配
fr空间: 将这710px的可用空间,分配给所有未被折叠且定义为fr的轨道 (这里是轨道 1, 2, 3,共 3 个)。- 计算每个
1fr的值:710px / 3≈ 236.67px。
- 计算每个
-
确定最终轨道宽度:
- 非空轨道 (1, 2, 3) 宽度 =
max(100px, 236.67px)≈ 237px (为方便计算,四舍五入或浏览器内部精度处理)。 - 空轨道 (4, 5, 6) 宽度 = 0px。
- 非空轨道 (1, 2, 3) 宽度 =
-
-
“图示” (3 个 Item,容器宽 730px):
容器 (Container: 730px wide) | 使用 auto-fit | gap: 10px | track: minmax(100px, 1fr) 计算: - 潜力列数 N=6 (用于判断是否折叠). - 空轨道 (4,5,6) 被折叠. - fr 可用空间 = 730px - (3-1)*10px (实际渲染的gap) = 710px. - 1fr = 710px / 3 (非空轨道数) ≈ 236.7px. - 非空列最终宽度 ≈ 237px. - 渲染总宽 = 3 * (710/3)px + 2 * 10px = 710px + 20px = 730px. -> **精确填满!** 布局示意: +-------------------------------------------------------------------+ -- 730px -- | +-----------------+ 10 +-----------------+ 10 +-----------------+ | | | Item 1 |--->| Item 2 |--->| Item 3 | | | | (≈237px) | | (≈237px) | | (≈237px) | | | +-----------------+ +-----------------+ +-----------------+ | +------------------------------------------------------------------—+ |<-------------------- 3个项目轨道 + 2个gap 精确填满容器宽度 ----------—>| (空轨道被彻底忽略,剩余空间由非空 fr 轨道完全瓜分) -
结果说明 :
-
auto-fit在折叠了空轨道后,有效地将容器的宽度(减去实际渲染的 gap)完全交给了剩余的有内容的fr轨道去分配。 -
因此,这 3 个轨道会拉伸,每个宽度约为 237px,它们加上之间的 2 个 10px 的 gap,总宽度正好等于 730px,完美地填满了容器。
-
auto-fit在分配fr空间时,其计算基础是容器宽度减去最终实际渲染的 gap 数量所占的宽度,而不是减去所有潜在 gap 的宽度。
-
总结关键区别:
| 特性 | auto-fill (自动填充) | auto-fit (自动适应) |
|---|---|---|
| 空轨道处理 | 保留空轨道 | 折叠空轨道 (最终宽度视为0) |
fr 空间分配给 | 所有潜力轨道 (包括空轨道) | 仅实际有内容的非空轨道 |
| 视觉效果 | 末尾可能因空轨道占用fr空间而留有明显空白 | 内容轨道显著拉伸以更好地利用空间,效果通常更“贴合”实际内容 |
| 主要侧重点/结果 | 倾向于保持潜在的轨道数量和结构 | 倾向于让实际内容项最大化地利用和填充可用空间 |
核心记忆点:
auto-fill:Fill with potential tracks, even if empty (可能留白).auto-fit:Fit the actual content, collapsing empty tracks (内容项更贴合容器).
通常,当你希望项目能自动换行并尽可能填满容器宽度时,auto-fit 是更常用的选择。
12. 对齐 (Alignment)
Grid 的对齐模型非常强大,借鉴并扩展了 Flexbox 的对齐能力,主要分为两大类:
- 对齐网格项目 (Aligning Grid Items): 控制单个网格项目(Grid Item)在其所在的网格区域(Grid Area) 内的位置。
- 对齐网格轨道 (Aligning Grid Tracks): 控制整个网格(所有轨道作为一个整体) 在 网格容器(Grid Container) 内的位置。
在讲解具体属性前,先理解两个轴:
- Inline Axis (行内轴 / 主轴): 通常是水平方向(从左到右)。与
justify-*系列属性相关。 - Block Axis (块轴 / 交叉轴): 通常是垂直方向(从上到下)。与
align-*系列属性相关。
一、 对齐网格项目 (Aligning Items within their Area)
这组属性决定了一个网格项目在其被分配到的网格区域(可能是一个单元格,也可能是跨越多个单元格的区域)中如何对齐。
1. justify-self
-
作用: 控制单个网格项目在其网格区域内沿 行内轴(水平方向) 的对齐方式。
-
应用在: 网格项目 (Grid Item) 上。
-
常用值:
-
stretch(默认值): 项目沿行内轴拉伸以填满其网格区域的宽度。 -
start: 项目在其网格区域内起始边对齐(通常是左对齐)。 -
end: 项目在其网格区域内结束边对齐(通常是右对齐)。 -
center: 项目在其网格区域内居中对齐。
-
-
“图示”理解:
场景设定: 假设网格项目(Item)被分配到了一个宽度大于其自身内容或设定宽度的网格区域(Grid Area)中。
-
justify-self: stretch;(默认值)-
效果: 项目会忽略自身的宽度设定(除非有
max-width等限制),被拉伸以填满其网格区域的整个宽度。 -
“图示”:
-
<-------- 网格区域宽度 (Grid Area Width) -------> +---------------------------------------------+ |[============== ITEM (被拉伸) ===============]| <-- 项目宽度被拉伸,填满区域 +---------------------------------------------+-
justify-self: start;-
效果: 项目不拉伸,保持其原有宽度(或内容宽度),并在其网格区域的起始位置(通常是左侧)对齐。
-
“图示”:
-
<-------- 网格区域宽度 (Grid Area Width) -------> +---------------------------------------------+ |[ ITEM ].....................................| <-- 项目靠左, "." 代表右侧空闲空间 +---------------------------------------------+-
justify-self: end;-
效果: 项目不拉伸,保持其原有宽度,并在其网格区域的结束位置(通常是右侧)对齐。
-
“图示”:
-
<-------- 网格区域宽度 (Grid Area Width) -------> +---------------------------------------------+ |.....................................[ ITEM ]| <-- 项目靠右, "." 代表左侧空闲空间 +---------------------------------------------+-
justify-self: center;-
效果: 项目不拉伸,保持其原有宽度,并在其网格区域内水平居中对齐。
-
“图示”:
-
<-------- 网格区域宽度 (Grid Area Width) --------> +---------------------------------------------—+ |...................[ ITEM ]...................| <-- 项目居中, "." 代表两侧等量空闲空间 +---------------------------------------------—+ -
2. align-self
-
作用: 控制单个网格项目在其网格区域内沿 块轴(垂直方向) 的对齐方式。
-
应用在: 网格项目 (Grid Item) 上。
-
常用值:
-
stretch(默认值): 项目沿块轴拉伸以填满其网格区域的高度。 -
start: 项目在其网格区域内起始边对齐(通常是顶部对齐)。 -
end: 项目在其网格区域内结束边对齐(通常是底部对齐)。 -
center: 项目在其网格区域内居中对齐。 -
baseline: 项目基于其内容的第一行(或最后一行)文字的基线进行对齐(需要内容配合)。
-
-
"图示"理解:
场景设定: 网格区域 (Grid Area) 自身有一定高度,网格项目 (Item) 的内容或设定高度小于该区域高度。
-
align-self: stretch;(默认值)-
效果: 项目会忽略自身的高度设定(除非有
max-height等限制),被拉伸以填满其网格区域的整个高度。 -
“图示”:
-
+-------------+ <-- 网格区域顶部 |=============| <-- 项目被拉伸,顶部贴边 | ITEM | | (stretch) | |=============| <-- 项目被拉伸,底部贴边 +-------------+ <-- 网格区域底部-
align-self: start;-
效果: 项目不拉伸,保持其原有高度(或内容高度),并在其网格区域的起始位置(通常是顶部)对齐。
-
“图示”:
-
+-------------+ <-- 网格区域顶部 | +---------+ | <-- 项目顶部贴边 | | ITEM | | | +---------+ | | | | Empty Space | <-- 项目下方是区域内的空闲空间 | | +-------------+ <-- 网格区域底部-
align-self: end;-
效果: 项目不拉伸,保持其原有高度,并在其网格区域的结束位置(通常是底部)对齐。
-
“图示”:
-
+-------------+ <-- 网格区域顶部 | | | Empty Space | <-- 项目上方是区域内的空闲空间 | | | +---------+ | | | ITEM | | | +---------+ | <-- 项目底部贴边 +-------------+ <-- 网格区域底部-
align-self: center;-
效果: 项目不拉伸,保持其原有高度,并在其网格区域内垂直居中对齐。
-
“图示”:
-
+-------------+ <-- 网格区域顶部 | | <-- 上方空闲空间 | +---------+ | | | ITEM | | <-- 项目垂直居中 | +---------+ | | | <-- 下方空闲空间 (与上方相等) +-------------+ <-- 网格区域底部 -
3. justify-items 和 align-items (容器属性)
- 作用: 这两个属性应用在网格容器 (Grid Container) 上,用于设置容器内所有网格项目的默认
justify-self和align-self值。 justify-items: 设置所有项目的默认水平对齐(相当于为每个项目设置justify-self)。align-items: 设置所有项目的默认垂直对齐(相当于为每个项目设置align-self)。- 值: 与
justify-self/align-self的值相同 (stretch,start,end,center,baseline(仅 align-items))。 - 覆盖: 如果在某个具体的网格项目上单独设置了
justify-self或align-self,它将覆盖从容器继承的justify-items或align-items的默认值。
示例:
.container {
display: grid;
height: 200px;
width: 300px;
grid-template-columns: 100px 100px;
grid-template-rows: 80px 80px;
/* 设置所有项目默认水平居中,垂直靠下 */
justify-items: center;
align-items: end;
border: 1px solid blue;
}
.item1 {
background: lightcoral;
/* 单独覆盖默认值,让此项在垂直方向上拉伸 */
align-self: stretch;
}
.item2 { background: lightblue; }
在这个例子中,.item2 会水平居中、垂直靠下(继承容器设置),而 .item1 会水平居中(继承),但垂直方向会拉伸(覆盖了 align-items: end)。
二、 对齐网格轨道 (Aligning Tracks within the Container)
这组属性用于控制整个网格轨道系统(所有行和列作为一个整体)在网格容器中的对齐。这只有在网格轨道的总尺寸(加上 gap)小于网格容器的尺寸时才会有效果。如果你的轨道(比如用了 fr 单位)已经占满了容器,那么这些属性可能看不到效果。
4. justify-content
-
作用: 控制整个网格内容沿 行内轴(水平方向) 在容器内的对齐方式和空间分布。
-
应用在: 网格容器 (Grid Container) 上。
-
常用值:
-
start: 网格在容器内起始边对齐(通常是左对齐)。 -
end: 网格在容器内结束边对齐(通常是右对齐)。 -
center: 网格在容器内居中对齐。 -
stretch: 如果轨道尺寸是auto,会拉伸它们以填满容器(但对px,fr等固定或弹性尺寸无效)。 -
space-between: 网格轨道沿行内轴均匀分布,第一个轨道靠起始边,最后一个轨道靠结束边,轨道之间的空间相等。 -
space-around: 网格轨道沿行内轴均匀分布,轨道两侧的空间是轨道之间空间的一半。 -
space-evenly: 网格轨道沿行内轴均匀分布,轨道之间及轨道与容器边缘的空间都相等。
-
-
“图示”理解 (假设容器很宽,网格本身较窄):
场景设定: 假设网格容器 (用
+---+边框表示) 的宽度大于其内部所有列轨道(用[Trk]代表一列)及它们之间gap的总宽度。也就是说,容器在水平方向上有剩余的空闲空间。我们用.代表空闲空间。-
justify-content: start;-
效果: 整个网格内容(所有列作为一个块)在容器的起始位置(通常是左侧)对齐。
-
“图示”:
-
<--------- Container Width --------> +----------------------------------+ |[Trk1][Trk2][Trk3]................| <-- 网格内容靠左, "." 代表右侧空闲空间 +----------------------------------+-
justify-content: end;-
效果: 整个网格内容(所有列作为一个块)在容器的结束位置(通常是右侧)对齐。
-
“图示”:
-
<-------- Container Width --------> +---------------------------------+ |...............[Trk1][Trk2][Trk3]| <-- 网格内容靠右, "." 代表左侧空闲空间 +---------------------------------+-
justify-content: center;-
效果: 整个网格内容(所有列作为一个块)在容器中水平居中。
-
“图示”:
-
<--------- Container Width ----------> +------------------------------------+ |.........[Trk1][Trk2][Trk3].........| <-- 网格内容居中, "." 代表两侧等量空闲空间 +------------------------------------+-
justify-content: space-between;-
效果: 第一个列轨道贴紧容器左侧,最后一个列轨道贴紧容器右侧,剩余的列轨道(连同它们之间的
gap)在中间均匀分布空闲空间。 -
“图示”: (点
.的数量仅示意相对空间)
-
<--------- Container Width --------> +----------------------------------+ |[Trk1]........[Trk2]........[Trk3]| <-- 两端贴边,中间轨道间距(.)相等 +----------------------------------+-
justify-content: space-around;-
效果: 所有列轨道均匀分布,且每列轨道两侧的空间相等。这意味着列轨道之间的间距是第一列左侧/最后一列右侧空间的两倍。
-
“图示”: (点
.的数量示意相对比例,中间点数约是边缘两倍)
-
<------- Container Width ------> +------------------------------+ |..[Trk1]....[Trk2]....[Trk3]..| <-- 边缘间距(..)是中间(....)一半 +------------------------------+-
justify-content: space-evenly;-
效果: 所有列轨道均匀分布,且所有间距(包括第一列左侧、列与列之间、最后一列右侧)完全相等。
-
“图示”: (点
.的数量示意所有间距相等)
-
<------- Container Width ------> +------------------------------+ |...[Trk1]...[Trk2]...[Trk3]...| <-- 所有间距(...)均等 +------------------------------+ -
5. align-content
-
作用: 控制整个网格内容沿 块轴(垂直方向) 在容器内的对齐方式和空间分布。
-
应用在: 网格容器 (Grid Container) 上。
-
常用值: 与
justify-content类似,但作用于垂直方向:start,end,center,stretch,space-around,space-between,space-evenly,baseline(较少用)。 -
图示理解
场景设定: 假设网格容器 (用
+---+边框表示) 的高度大于其内部所有网格行轨道(用---ROW---代表一行)及它们之间gap的总高度。也就是说,容器在垂直方向上有剩余的空闲空间。-
align-content: start;-
效果: 整个网格内容(所有行作为一个块)在容器的起始位置(通常是顶部)对齐。
-
“图示”:
-
+-----------------+ <-- 容器顶部 | --- ROW 1 --- | <-- 网格内容块 靠上 | --- ROW 2 --- | | --- ROW 3 --- | |.................| |..Empty Space....| <-- 下方是容器内的空闲空间 |.................| +-----------------+ <-- 容器底部-
align-content: end;-
效果: 整个网格内容(所有行作为一个块)在容器的结束位置(通常是底部)对齐。
-
“图示”:
-
+-----------------+ <-- 容器顶部 |.................| |..Empty Space....| <-- 上方是容器内的空闲空间 |.................| | --- ROW 1 --- | | --- ROW 2 --- | <-- 网格内容块 靠下 | --- ROW 3 --- | +-----------------+ <-- 容器底部-
align-content: center;-
效果: 整个网格内容(所有行作为一个块)在容器中垂直居中。
-
“图示”:
-
+-----------------+ <-- 容器顶部 |.................| <-- 上方空闲空间 |..Empty Space....| | --- ROW 1 --- | | --- ROW 2 --- | <-- 网格内容块 居中 | --- ROW 3 --- | |..Empty Space....| |.................| <-- 下方空闲空间 (与上方相等) +-----------------+ <-- 容器底部-
align-content: space-between;-
效果: 第一行轨道贴紧容器顶部,最后一行轨道贴紧容器底部,剩余的行轨道(连同它们之间的
gap)在中间均匀分布空闲空间。 -
“图示”:
-
+-----------------+ | --- ROW 1 --- | <-- 贴紧顶部 |.................| |..Equal Space....| <-- 行轨道间的等距空白 |.................| | --- ROW 2 --- | |.................| |..Equal Space....| <-- 行轨道间的等距空白 |.................| | --- ROW 3 --- | <-- 贴紧底部 +-----------------+-
align-content: space-around;-
效果: 所有行轨道均匀分布,且每行轨道两侧的空间相等。这意味着行轨道之间的间距是第一行轨道上方/最后一行轨道下方空间的两倍。
-
“图示” (X 代表一个单位空间):
-
+-----------------+ | ..Space (X).... | <-- 顶部空间 X | --- ROW 1 --- | | ..Space (2X)... | <-- 行间距 2X | --- ROW 2 --- | | ..Space (2X)... | <-- 行间距 2X | --- ROW 3 --- | | ..Space (X).... | <-- 底部空间 X +-----------------+-
align-content: space-evenly;-
效果: 所有行轨道均匀分布,且所有间距(包括第一行轨道上方、行与行之间、最后一行轨道下方)完全相等。
-
“图示” (Y 代表一个单位空间):
-
+-----------------+ | ..Space (Y).... | <-- 顶部空间 Y | --- ROW 1 --- | | ..Space (Y).... | <-- 行间距 Y | --- ROW 2 --- | | ..Space (Y).... | <-- 行间距 Y | --- ROW 3 --- | | ..Space (Y).... | <-- 底部空间 Y +-----------------+-
align-content: stretch;(默认值)- 效果: 如果行轨道的高度被设置为
auto,它们会被拉伸以试图填满容器的整个高度(同时考虑gap)。如果行轨道有固定高度或使用了fr单位已经占满空间,则此值可能无视觉效果。 - (无特定图示,效果依赖于轨道尺寸定义)
- 效果: 如果行轨道的高度被设置为
-
总结:
justify-self/align-self(或容器上的justify-items/align-items) 控制 “坑位里的人怎么站” (项目在自己的区域里如何对齐)。justify-content/align-content控制 “所有坑位组成的队伍怎么在广场上站” (整个网格在容器里如何对齐和分布,前提是广场比队伍大)。
掌握这些对齐属性对于精确控制 Grid 布局的外观至关重要。它们的浏览器支持度在现代浏览器中也非常好(IE 除外)。
总结:
CSS Grid 是一个功能极其强大的布局工具,它提供了一种清晰、可预测的方式来创建复杂的二维布局。掌握其核心概念(容器、项目、线、轨道、单元、区域)和基本属性(display: grid, grid-template-*, gap, grid-column/row/area)是使用它的关键。虽然初看起来概念较多,但一旦理解,它会让许多以前难以实现的布局变得简单。
想要了解Grid更加全面的内容,前往阮一峰老师的教程。