CSS 教程 / CSS Grid:repeat、auto-fit、auto-fill 与命名区域
CSS Grid 可以用 repeat() 重复轨道,并通过 auto-fit 或 auto-fill 调整列数。两种自动重复模式在处理空轨道的方式上有所不同;命名区域提供了描述更大页面区域的另一种方式。
比较固定重复和自动重复
.cards {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
}
repeat(3, ...) 定义了三个列,不管卡片的数量是多少。第四个自动放置的项目会进入下一行;它不会增加列数。这与自动重复不同,自动重复会计算可以容纳多少轨道。参见 CSS Grid 基础:轨道、线和项目放置。
.cards {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 192px), 1fr));
}
auto-fit 会根据指定的最小尺寸计算可以容纳多少列。minmax 的第一个值是最小值,第二个值是最大值,1fr 分配剩余空间。在这里 min(100%, 192px) 保证最小列宽不会大于其父元素。将列的最小值固定为 192px,即使只有一列,当父元素宽度小于 192px 时也可能超出父元素。
对于一个800像素的容器,192像素的最小列宽和16像素的间隙,三列需要608像素,而四列需要816像素。只能容纳三列。这个计算决定了列数;随后的空白空间分配决定它们的最终宽度。长文本仍然需要换行策略。
比较 auto-fill 和 auto-fit
| 值 | 在放置项目后重复的空轨道 | 少量项目的大屏幕 |
|---|---|---|
| auto-fill | 保留空轨道 | 保留空列空间 |
| auto-fit | 折叠没有项目的重复轨道 | 占用的轨道扩展到可用空间 |
两者都会先计算容器能容纳的重复列数。auto-fit 在放置项目后还会折叠没有项目占用或跨越的重复轨道。如果项目填满所有列,这两个值看起来可能很相似,因此在比较时使容器更宽并减少卡片数量。当窄屏幕只能容纳一列时,也通常难以观察到差异。自动重复规则详见MDN:repeat()。
了解自动放置与 dense 填补空隙
未指定位置的项目将根据默认的 grid-auto-flow: row 按行排列。即使前一个项目占据了较大的区域并产生了空白空间,默认的排列方式在放置下一个项目时也不会持续回到空白空间。这种默认行为使 HTML 的排序和视觉进程更容易理解。
grid-auto-flow: row dense 可以用后面的较小项目填充前面的空白空间。虽然空间变得更紧,但它在屏幕上显示的顺序与 DOM(文档对象模型)的顺序可能不同。CSS 布局不会重新排序 HTML,因此必须同时考虑键盘焦点和阅读顺序。对于有意义的编号步骤、时间顺序记录和表单条目,保持顺序比消除空白更重要。你可以在 MDN 的 grid-auto-flow 描述 中查看自动布局的条件。
运行自动重复示例
将示例保存为 grid-auto-repeat.html,以 UTF-8 编码保存并在浏览器中打开。第一部分有两张卡片,容器最大宽度为 800px。第二部分使用两条跨两列的链接和两条跨一列的链接,留下一个空隙,让你可以将普通布局与 dense 进行比较。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>grid-auto-repeat.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 192px), 1fr));
gap: 16px; width: min(100%, 800px); }
.cards > div { min-width: 0; padding: 16px; background: #dceefa; overflow-wrap: anywhere; }
.flow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px;
grid-auto-flow: row; max-width: 600px; margin-top: 16px; }
.flow > a { padding: 12px; background: #e8f5de; }
.flow > .wide { grid-column: span 2; }
</style>
</head>
<body>
<h1>Comparing empty repeated columns</h1><div class="cards"><div>First card</div><div>Second card</div></div>
<h2>Default auto-placement</h2><div class="flow"><a class="wide" href="#result">1: two spaces</a><a class="wide" href="#result">2: two spaces</a><a href="#result">3: One space</a><a href="#result">4: one space</a></div>
<p id="result">This is the destination of the automatic-layout link.</p>
</body>
</html>
- 在宽屏上,如果第一部分为 800px,auto-fit 中的两张卡片将各占 392px。仅将 auto-fit 替换为 auto-fill 会保留三列,使前两列的卡片大约为 256px,留下一个空列。确认无误后,恢复为 auto-fit。
- 将更多的 div 复制到第一个部分,使其总共四个。加宽窗口使容器达到 800px,此时三列均被占用,第四张卡片进入下一行。在这种情况下,没有空的重复列,从而减少了两个值之间的差异。练习后,返回到原来的两张卡片。
- 缩小屏幕以确保父容器的宽度小于192px。得益于 min(100%, 192px),一列会适应其父容器。将最小值改为仅192px可能会在空间较紧的区域引起溢出。对比之后,恢复原始表达式。
- 检查 .flow 中链接 1 后的空白以及链接 3 的位置。如果只将 grid-auto-flow 更改为 row dense,数字 3 将被放置在之前的空白处。使用 Tab 检查顺序 1 → 2 → 3 → 4 是否保持。练习后返回 row。
使用命名区域定义页面区域
与其单独计算列和行号,不如通过使用诸如 header·main·aside·footer 之类的名称来表达大块区域,从而更容易理解页面布局意图。grid-template-areas 中的每个字符串都是一行,字符串中以空格分隔的名称是单元格。子元素的 grid-area 值必须与其名称匹配才能进入该区域。如何根据屏幕宽度更改列配置在 CSS 媒体查询和基于内容的断点 中进行了说明。
在多个单元格中重复相同的名称会创建一个矩形区域。区域不能呈L形或孤立的小岛形状。每一行必须有相同数量的单元格,留空的单元格用点表示(.)。如果声明无效,浏览器将不会执行,这可能会看起来像是自动放置。你可以在MDN的grid-template-areas描述中查看有效区域条件。
将下面的代码保存为grid-areas.html,以 UTF-8 编码保存并在浏览器中打开。在宽屏布局下,正文与辅助信息并排放置,而在640px或更小的屏幕上,它们会排列在一列。根据屏幕布局,HTML顺序没有反转,但主要文本出现在辅助信息之前。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>grid-areas.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.page { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
grid-template-areas: "header header" "main aside" "footer footer"; gap: 16px; max-width: 1000px; }
.page > * { min-width: 0; padding: 20px; background: #dceefa; overflow-wrap: anywhere; }
header { grid-area: header; } main { grid-area: main; } aside { grid-area: aside; } footer { grid-area: footer; }
@media (max-width: 640px) {
.page { grid-template-columns: minmax(0, 1fr); grid-template-areas: "header" "main" "aside" "footer"; }
}
</style>
</head>
<body>
<h1>Page area names</h1><div class="page"><header>Site title</header><main><h2>Main content</h2><p>This is the main content area. The HTML order keeps it first on mobile, too.</p></main><aside>Related information</aside><footer>Contact and site information</footer></div>
</body>
</html>
- 确保页眉和页脚在宽屏幕上占据两列。如果将屏幕缩小到640px或更小,布局将变为一列:header → main → aside → footer。请注意为什么需要同时更改列定义和区域字符串。
- 仅将aside的grid-area更改为不同的名称时,模板中为aside保留的区域与实际元素之间可能会断开连接,元素可能不会放置在预期的插槽中。请将名称恢复为aside。
- 如果只更改宽屏幕模板为"header header" "header aside" "footer footer",页眉将呈L形,并且grid-template-areas声明将无效。在开发者工具中确认应用后,请恢复原始的三条字符串。
在实践中,如果你将创建列的区域(基于卡片数量)与命名页面部分的区域分开,会更容易维护。自动重复决定了数量和最小宽度,命名区域描述页面结构,媒体查询决定何时切换布局。在尝试将两者强行合并为一个规则之前,先决定每个容器试图解决什么问题。




