在传统的表格布局时代,合并单元格靠的是colspan和rowspan属性,写起来直观但灵活性差。到了CSS Grid时代,合并单元格的能力被彻底解放出来:任何一个网格项都可以自由地横跨多列、纵跨多行,甚至精确摆放到网格的任意角落。这篇文章就来详细讲解Grid布局中创建组合单元格的几种主流方法,包括span跨越、命名网格线定位和区域命名法,并分析它们各自适合的使用场景。

方法一:使用span关键字实现跨行跨列
这是最常用也是最简单的方式。Grid为每个网格项提供了四个核心属性:grid-column-start、grid-column-end、grid-row-start和grid-row-end,分别控制元素在列方向和行方向的起始与结束位置。当结束位置写成span n的形式时,就表示从起点开始跨越n个轨道。
先看一个基础例子,让第一个元素横跨两列:
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
}
.item1 {
grid-column: 1 / span 2; /* 从第1条列网格线开始,跨越2列 */
grid-row: 1;
}
.item2 {
grid-column: 3;
grid-row: 1;
}这里的grid-column: 1 / span 2是简写形式,等价于grid-column-start: 1; grid-column-end: span 2;。span的语义非常明确:不管起点在哪里,都往后延伸指定数量的轨道。因此在响应式布局中,当你不确定元素的具体位置时,用span比用绝对网格线编号更稳妥。
如果只想跨越一列,也可以直接写grid-column: span 2,省略起点后浏览器会自动寻找第一个可用位置放置元素。这在配合自动放置算法时特别有用,例如让某些卡片占两个格子,其余占一个格子,浏览器会自动把大卡片塞进合适的位置。
方法二:使用命名网格线精确定位
除了数字编号,网格线还可以命名。在定义grid-template-columns时,用方括号给网格线起名字,之后在网格项上就可以引用这些名字来定位和跨越。这种方式在大型的、结构固定的布局中可读性更好。
.container {
display: grid;
grid-template-columns: [main-start] 1fr [content-start] 1fr [content-end] 1fr [main-end];
grid-template-rows: [header-start] 80px [header-end] 1fr [footer-start] 60px [footer-end];
}
.header {
grid-column: main-start / main-end; /* 横跨所有三列 */
grid-row: header-start / header-end;
}
.sidebar {
grid-column: content-start / content-end;
}命名网格线的好处是语义化。当布局变得复杂,比如一个页面有十几条网格线时,grid-column: main-start / main-end显然比grid-column: 1 / 4更容易维护。此外,命名网格线和span可以混用,例如grid-column: content-start / span 2,从命名起点跨越两个轨道。
还有一个实用技巧:隐式命名。如果网格线的名字写成xxx-start和xxx-end的形式,浏览器会自动生成一个叫xxx的网格区域。反过来,定义了区域之后,也会自动生成对应的xxx-start和xxx-end网格线。这套机制正是下面要讲的区域命名法的基础。
方法三:grid-template-areas区域命名法
如果说前两种方法是让元素去找位置,那区域命名法就是先画好地图再对号入座。在容器上通过grid-template-areas用字符串描述整体布局,每个字符串代表一行,字符串中的每个名字代表一个单元格,相同的名字就会自动合并成一个组合单元格。
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: 80px 1fr 60px;
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
}
.header { grid-area: header; } /* 横跨三列 */
.sidebar { grid-area: sidebar; }
.main { grid-area: main; } /* 横跨两列 */
.footer { grid-area: footer; }上面的例子中,header名字在三行字符串中重复出现,天然形成了一个横跨三列的组合单元格;main出现了两次,横跨右侧两列。整个布局结构一眼就能看懂,完全不需要去数网格线编号。
使用区域命名法有几条规则必须注意:每个字符串的列数必须一致,否则整个声明无效;想要留空某个格子用点号.表示;区域必须是矩形的,不能出现L形或T形的区域,比如一个名字在第一行占第1、2列,在第二行只占第3列,这是不合法的。
隐式网格与自动填充的注意事项
当元素跨越的范围超出了显式定义的网格轨道时,浏览器会自动创建隐式轨道来容纳它。隐式轨道的尺寸由grid-auto-rows和grid-auto-columns控制,默认是auto。一个常见的坑是:某个元素写了grid-row: span 3,但容器只定义了两行,此时第三行会以隐式方式生成,高度可能和你预期的不一致,需要显式设置grid-auto-rows来统一。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 150px; /* 统一隐式行的高度 */
grid-auto-flow: dense; /* 开启密集填充,填补空洞 */
}
.item-featured {
grid-column: span 2; /* 特色卡片占两格 */
grid-row: span 2;
}另外,在自动填充布局中让某些元素跨越多列后,容易出现网格空洞。这时可以加上grid-auto-flow: dense,让浏览器回填后面较小的元素来填补空隙,代价是元素的视觉顺序可能和DOM顺序不一致,如果布局对顺序有要求(比如时间流列表)就要慎用。
三种方法如何选择
三种方式没有绝对的优劣,选择依据主要是布局的复杂度和团队习惯。简单的卡片流布局,用span跨越最省事;结构固定的页面级框架,区域命名法可读性最高;而命名网格线则介于两者之间,适合需要精确定位又想保留一定语义的场景。实际项目中也可以组合使用:容器用grid-template-areas规划大框架,内部局部元素再用span微调。
最后提醒一点,Grid的合并和表格的合并在思维上有本质区别:表格是先有格子再合并,Grid是先声明跨越范围,浏览器自动推导出网格结构。理解了这一点,面对复杂的组合单元格需求时,思路会清晰很多。建议把这些例子复制到浏览器里亲手改一改参数,观察网格线的变化,比单纯看文章印象深刻得多。