Grid 项目属性:精确控制布局中的每个元素
想象你在玩拼图游戏,游戏板给你提供了一个网格结构(这就是 Grid 容器),而每一块拼图(Grid 项目)可以占据一个或多个格子,可以放在任何位置。Grid 项目属性就是你的工具,让你可以精确地告诉每一块拼图:"你应该从第 2 行第 3 列开始,占据 2 行 3 列的空间"。
在上一章中,我们学习了如何用容器属性定义网格的整体结构。现在,让我们学习如何控制单个项目在这个结构中的位置和表现。
基于网格线的定位
Grid 最强大的特性之一是可以精确地指定项目应该占据哪些单元格。这是通过网格线编号实现的。
grid-column:控制列位置
grid-column 属性指定项目在列方向上的起始和结束位置。
基础用法
.item {
grid-column-start: 1; /* 从第 1 条垂直线开始 */
grid-column-end: 3; /* 到第 3 条垂直线结束 */
/* 相当于占据第 1 列和第 2 列 */
}更常用的是简写形式:
.item {
grid-column: 1 / 3; /* 从线 1 到线 3 */
}让我们看一个完整的例子:
<div class="grid">
<div class="item item-1">Item 1</div>
<div class="item item-2">Item 2</div>
<div class="item item-3">Item 3</div>
</div>.grid {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 4 列,有 5 条垂直线 */
grid-auto-rows: 100px;
gap: 10px;
background-color: #f5f5f5;
padding: 10px;
}
.item {
background-color: #2196f3;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.item-1 {
grid-column: 1 / 3; /* 占据第 1-2 列 */
background-color: #f44336;
}
.item-2 {
grid-column: 3 / 5; /* 占据第 3-4 列 */
background-color: #4caf50;
}
.item-3 {
grid-column: 1 / 5; /* 占据所有 4 列 */
background-color: #ff9800;
}在这个例子中:
- Item 1 横跨 2 列(从线 1 到线 3)
- Item 2 也横跨 2 列(从线 3 到线 5)
- Item 3 横跨所有 4 列(从线 1 到线 5)
使用 span 关键字
有时候你不关心具体从哪条线开始,只想让项目占据几列。这时可以使用 span 关键字:
.item {
grid-column: span 2; /* 占据 2 列,具体位置由自动放置决定 */
}你也可以指定起始线,然后用 span 说明要跨越几列:
.item {
grid-column: 2 / span 3; /* 从线 2 开始,跨越 3 列,到线 5 */
}或者反过来,指定结束线和跨越的列数:
.item {
grid-column: span 2 / 5; /* 跨越 2 列,到线 5,从线 3 开始 */
}实际应用:创建特色项目
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.card {
background-color: white;
border: 1px solid #ddd;
padding: 20px;
border-radius: 8px;
}
.card-featured {
grid-column: span 2; /* 特色卡片占据 2 列 */
background-color: #2196f3;
color: white;
font-size: 20px;
}负数索引
Grid 支持负数索引,用于从末尾开始计数。-1 表示最后一条线,-2 表示倒数第二条线,以此类推。
.item {
grid-column: 1 / -1; /* 从第一条线到最后一条线,占据所有列 */
}这在你不确定网格有多少列时特别有用:
.header {
grid-column: 1 / -1; /* 无论有多少列,都占据所有列 */
}grid-row:控制行位置
grid-row 的用法与 grid-column 完全相同,只是它控制的是行而不是列。
.item {
grid-row: 1 / 3; /* 从第 1 行到第 3 行,占据 2 行 */
}完整示例:创建复杂布局
<div class="grid">
<div class="item header">Header</div>
<div class="item sidebar">Sidebar</div>
<div class="item main">Main Content</div>
<div class="item aside">Aside</div>
<div class="item footer">Footer</div>
</div>.grid {
display: grid;
grid-template-columns: 200px 1fr 300px; /* 3 列 */
grid-template-rows: auto 1fr auto; /* 3 行 */
min-height: 100vh;
gap: 10px;
}
.item {
background-color: #2196f3;
color: white;
padding: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.header {
grid-column: 1 / -1; /* 横跨所有列 */
grid-row: 1; /* 第一行 */
background-color: #333;
}
.sidebar {
grid-column: 1; /* 第一列 */
grid-row: 2; /* 第二行 */
background-color: #4caf50;
}
.main {
grid-column: 2; /* 第二列 */
grid-row: 2; /* 第二行 */
background-color: white;
color: #333;
}
.aside {
grid-column: 3; /* 第三列 */
grid-row: 2; /* 第二行 */
background-color: #ff9800;
}
.footer {
grid-column: 1 / -1; /* 横跨所有列 */
grid-row: 3; /* 第三行 */
background-color: #333;
}这创建了一个经典的页面布局:顶部通栏 header,中间三栏,底部通栏 footer。
使用 span 跨越多行
就像 grid-column 一样,grid-row 也可以使用 span:
.sidebar {
grid-column: 1;
grid-row: 2 / span 2; /* 从第 2 行开始,跨越 2 行 */
}实际应用:图片画廊
<div class="gallery">
<div class="photo photo-1">Photo 1</div>
<div class="photo photo-2">Photo 2</div>
<div class="photo photo-3">Photo 3</div>
<div class="photo photo-4">Photo 4</div>
<div class="photo photo-5">Photo 5</div>
<div class="photo photo-6">Photo 6</div>
</div>.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 200px;
gap: 15px;
}
.photo {
background-size: cover;
background-position: center;
border-radius: 8px;
overflow: hidden;
}
/* 创建不同大小的照片 */
.photo-1 {
grid-column: span 2;
grid-row: span 2;
}
.photo-3 {
grid-column: span 2;
}
.photo-5 {
grid-row: span 2;
}这创建了一个动态的画廊布局,有些照片更大(占据更多空间),有些照片正常大小。
grid-area:位置简写
grid-area 是一个超级简写属性,可以同时指定行和列的起始和结束位置:
.item {
grid-area: row-start / column-start / row-end / column-end;
}例如:
.item {
grid-area: 1 / 2 / 3 / 4;
/* 相当于: */
/* grid-row: 1 / 3; */
/* grid-column: 2 / 4; */
/* 从第 1 行第 2 列开始,到第 3 行第 4 列结束 */
}虽然这很简洁,但顺序容易记错。我个人更喜欢分别使用 grid-row 和 grid-column,代码更清晰。
grid-area 还有另一个用途:配合命名网格区域使用,我们会在下一节详细讲解。
对齐控制
Grid 容器可以设置所有项目的默认对齐方式,但单个项目可以覆盖这些默认值。
justify-self:单个项目的水平对齐
justify-self 控制单个项目在其单元格中的水平对齐方式,覆盖容器的 justify-items 设置。
.grid {
display: grid;
grid-template-columns: repeat(3, 200px);
justify-items: start; /* 默认所有项目靠左 */
}
.item {
width: 100px;
background-color: #2196f3;
}
.item-centered {
justify-self: center; /* 这个项目居中,其他项目靠左 */
}
.item-right {
justify-self: end; /* 这个项目靠右,其他项目靠左 */
}可能的值:
start:靠左对齐end:靠右对齐center:水平居中stretch:拉伸填充单元格宽度
实际应用:按钮对齐
<div class="card">
<h3>Premium Plan</h3>
<p>$29/month</p>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
<li>Feature 3</li>
</ul>
<button class="btn-choose">Choose Plan</button>
</div>.card {
display: grid;
grid-template-rows: auto auto 1fr auto;
/* 标题、价格、特性列表、按钮 */
gap: 15px;
padding: 30px;
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
}
.btn-choose {
justify-self: stretch; /* 按钮拉伸到整个宽度 */
padding: 12px;
background-color: #2196f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}align-self:单个项目的垂直对齐
align-self 控制单个项目在其单元格中的垂直对齐方式,覆盖容器的 align-items 设置。
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 150px;
align-items: start; /* 默认所有项目靠上 */
}
.item {
background-color: #2196f3;
padding: 10px;
}
.item-centered {
align-self: center; /* 这个项目垂直居中 */
}
.item-bottom {
align-self: end; /* 这个项目靠下 */
}
.item-stretched {
align-self: stretch; /* 这个项目拉伸到整个单元格高度 */
}可能的值:
start:靠上对齐end:靠下对齐center:垂直居中stretch:拉伸填充单元格高度
place-self:对齐简写
place-self 是 align-self 和 justify-self 的简写:
.item {
place-self: center center;
/* 相当于: */
/* align-self: center; */
/* justify-self: center; */
/* 项目在单元格中完美居中 */
}如果只写一个值,两个方向都使用这个值:
.item {
place-self: center;
/* 水平和垂直都居中 */
}层叠顺序
当多个项目占据同一个单元格时,它们会重叠。默认情况下,后面的项目会覆盖前面的项目。
z-index:控制层叠顺序
你可以使用 z-index 控制重叠项目的层叠顺序:
<div class="grid">
<div class="item item-1">Item 1</div>
<div class="item item-2">Item 2</div>
<div class="item item-3">Item 3</div>
</div>.grid {
display: grid;
grid-template-columns: repeat(3, 150px);
grid-auto-rows: 150px;
gap: 10px;
}
.item {
background-color: #2196f3;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
border-radius: 8px;
border: 3px solid white;
}
.item-1 {
grid-column: 1 / 3;
grid-row: 1 / 3;
background-color: #f44336;
z-index: 1; /* 在最上层 */
}
.item-2 {
grid-column: 2 / 4;
grid-row: 1 / 2;
background-color: #4caf50;
z-index: 2; /* 在中间层 */
}
.item-3 {
grid-column: 2 / 4;
grid-row: 2 / 3;
background-color: #ff9800;
z-index: 3; /* 在最顶层 */
}在这个例子中,三个项目有重叠部分,z-index 值越大的项目显示在越上层。
实际应用:卡片堆叠效果
.card-stack {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
width: 300px;
height: 200px;
}
.card {
grid-column: 1;
grid-row: 1;
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, z-index 0s;
}
.card:nth-child(1) {
transform: rotate(-5deg);
z-index: 1;
}
.card:nth-child(2) {
transform: rotate(0deg);
z-index: 2;
}
.card:nth-child(3) {
transform: rotate(5deg);
z-index: 3;
}
.card:hover {
z-index: 10;
transform: scale(1.05) rotate(0deg);
}这创建了一个卡片堆叠效果,鼠标悬停时卡片会浮到最上层并放大。
实际应用示例
让我们组合使用这些属性创建一些实用的布局。
杂志式文章布局
<article class="magazine-article">
<h1 class="title">Breaking: Major Discovery in Science</h1>
<img class="hero-image" src="hero.jpg" alt="Hero" />
<p class="intro">This is the introduction paragraph...</p>
<p class="content">Main content goes here...</p>
<aside class="pullquote">"This is a pull quote"</aside>
<p class="content">More content...</p>
<figure class="side-image">
<img src="side.jpg" alt="Side" />
<figcaption>Image caption</figcaption>
</figure>
<p class="content">Even more content...</p>
</article>.magazine-article {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; /* 6 列 */
gap: 20px;
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
}
.title {
grid-column: 1 / -1; /* 占据所有列 */
font-size: 48px;
margin: 0 0 30px 0;
}
.hero-image {
grid-column: 1 / -1; /* 占据所有列 */
width: 100%;
height: 400px;
object-fit: cover;
border-radius: 8px;
}
.intro {
grid-column: 1 / 5; /* 占据左边 4 列 */
font-size: 20px;
font-weight: 500;
color: #666;
}
.content {
grid-column: 1 / 5; /* 主要内容占据左边 4 列 */
line-height: 1.8;
}
.pullquote {
grid-column: 5 / -1; /* 引用占据右边 2 列 */
grid-row: 4 / 6; /* 跨越 2 行 */
align-self: start;
background-color: #f5f5f5;
padding: 30px;
border-left: 4px solid #2196f3;
font-size: 24px;
font-style: italic;
color: #333;
}
.side-image {
grid-column: 5 / -1; /* 边侧图片占据右边 2 列 */
align-self: start;
margin: 0;
}
.side-image img {
width: 100%;
border-radius: 8px;
}
.side-image figcaption {
margin-top: 10px;
font-size: 14px;
color: #666;
text-align: center;
}这创建了一个专业的杂志式布局,主要内容在左边宽栏,引用和图片在右边窄栏。
产品展示网格
<div class="product-showcase">
<div class="product featured">
<h2>Featured Product</h2>
<p>Our best seller</p>
</div>
<div class="product">Product 2</div>
<div class="product">Product 3</div>
<div class="product">Product 4</div>
<div class="product">Product 5</div>
<div class="product">Product 6</div>
<div class="product">Product 7</div>
<div class="product">Product 8</div>
</div>.product-showcase {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 200px;
gap: 15px;
padding: 20px;
}
.product {
background-color: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: transform 0.3s, box-shadow 0.3s;
}
.product:hover {
transform: translateY(-5px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.product.featured {
grid-column: span 2; /* 横跨 2 列 */
grid-row: span 2; /* 横跨 2 行 */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 24px;
}
.product.featured h2 {
margin: 0 0 10px 0;
font-size: 32px;
}仪表板小部件
<div class="dashboard">
<div class="widget stats">
<h3>Statistics</h3>
<p>1,234 users</p>
</div>
<div class="widget chart">
<h3>Sales Chart</h3>
<div class="chart-area">Chart goes here</div>
</div>
<div class="widget activity">
<h3>Recent Activity</h3>
<ul>
<li>User logged in</li>
<li>Order placed</li>
<li>Payment received</li>
</ul>
</div>
<div class="widget notifications">
<h3>Notifications</h3>
<p>3 new messages</p>
</div>
</div>.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-auto-rows: minmax(150px, auto);
gap: 20px;
padding: 20px;
background-color: #f5f5f5;
}
.widget {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.widget h3 {
margin: 0 0 15px 0;
color: #333;
font-size: 18px;
border-bottom: 2px solid #2196f3;
padding-bottom: 10px;
}
/* 让图表小部件更大 */
.chart {
grid-column: span 2; /* 横跨 2 列 */
grid-row: span 2; /* 横跨 2 行 */
}
.chart-area {
height: 200px;
background-color: #f5f5f5;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
}
/* 让活动列表稍高一些 */
.activity {
grid-row: span 2;
}
.activity ul {
list-style: none;
padding: 0;
margin: 0;
}
.activity li {
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.activity li:last-child {
border-bottom: none;
}常见问题和技巧
项目重叠时的处理
当项目重叠时,记住以下几点:
- 默认层叠顺序:后面的项目覆盖前面的项目
- 使用 z-index:显式控制层叠顺序
- 考虑透明度:使用
opacity或rgba颜色让重叠可见 - 添加交互:用
:hover状态改变z-index
响应式调整
单个项目可以在不同屏幕尺寸下占据不同的空间:
.featured-item {
grid-column: span 2;
grid-row: span 2;
}
@media (max-width: 768px) {
.featured-item {
grid-column: span 1; /* 在小屏幕上只占 1 列 */
grid-row: span 1; /* 只占 1 行 */
}
}命名的网格线
你可以给网格线命名,让代码更有语义:
.grid {
display: grid;
grid-template-columns: [sidebar-start] 200px [sidebar-end main-start] 1fr [main-end];
}
.sidebar {
grid-column: sidebar-start / sidebar-end;
}
.main {
grid-column: main-start / main-end;
}这让代码更自解释,维护更容易。
总结
Grid 项目属性给了你精确控制每个元素的能力。让我们回顾核心概念:
定位属性:
grid-column:控制列位置(简写:grid-column-start/grid-column-end)grid-row:控制行位置(简写:grid-row-start/grid-row-end)grid-area:同时控制行和列(或命名区域)
对齐属性:
justify-self:单个项目的水平对齐align-self:单个项目的垂直对齐place-self:对齐简写
其他:
z-index:控制重叠项目的层叠顺序
关键技巧:
- 使用网格线编号精确定位
- 使用
span关键字指定跨越的行/列数 - 使用负数索引从末尾开始计数
- 混合使用自动放置和手动定位
- 用
-self属性覆盖容器的默认对齐
记住:
- 容器属性定义整体布局规则
- 项目属性让单个元素脱颖而出
- 组合使用两者创建复杂而灵活的布局
在下一节中,我们将学习 Grid 模板区域(Grid Template Areas),这是一种更直观、更语义化的定义布局的方法。