第六章:CSS 布局:从 Flexbox 到 Grid 的布局之道

第六章:CSS 布局:从 Flexbox 到 Grid 的布局之道

摘要: 欢迎来到 CSS 世界的“终局之战”——布局。可以毫不夸张地说,掌握了布局,就等于掌握了 99% 的 CSS。本章是一个内容极其丰富的“大章节”,我们将系统性地学习从一维的 Flexbox 到二维的 Grid 这两大现代布局利器,并掌握 Position 定位的精准微操与 Multi-column 的巧妙应用。更重要的是,我们会将所有这些技术融合进“响应式设计”的思维框架中,让您有能力构建适应任何屏幕尺寸的现代化网页。请务必多加练习,本章的知识将是您未来前端生涯中最宝贵的财富。

学习路径说明: 您会注意到,本章我们将重点讲解 Flexbox 和 Grid,而对 float (浮动) 等传统技术则一笔带过。这是因为,float 最初是为“图文环绕”而设计的,用它来做全局布局是一种“滥用”,会带来层叠塌陷、需要清除浮动等无穷的副作用,在 2025 年的今天,它已被现代布局方案完全取代。我们的目标是直接学习当前业界的最佳实践,而非背负历史包袱。


在本章的学习地图中,我们将一步步成为布局大师:

  1. 首先,我们将掌握 一维布局的王者——Flexbox,完美解决组件内的对齐问题。
  2. 接着,我们将学习 二维布局的终极方案——Grid,随心所欲地绘制整个页面蓝图。
  3. 然后,我们会探索 多列布局 (Multi-column) 的妙用,用最简单的代码实现瀑布流。
  4. 之后,我们将学习 Position 定位,掌握脱离常规文档流的“魔法”。
  5. 最后,我们将所有知识融会贯通,学习 响应式设计的核心,让我们的页面在手机、平板和桌面端都能完美呈现。

6.1. Flexbox 弹性布局:一维空间的统治者

Flexbox 是为 一维布局 而生的。无论是水平行内还是垂直列内,只要你需要在一条轴线上对齐、分布、排序一组元素,Flexbox 就是最简单、最强大的工具。

6.1.1. 核心概念:主轴与交叉轴

一旦为一个元素设置了 display: flex;,它就成为了一个 Flex 容器 (flex container),其所有直接子元素都自动成为 Flex 项目 (flex item)

容器内部存在两根无形的轴:

  • 主轴 (main axis): 所有项目默认沿着主轴排列。默认是水平方向。
  • 交叉轴 (cross axis): 与主轴垂直的轴。默认是垂直方向。

6.1.2. 容器属性 (Flex Container)

这些属性写在 父元素 上,用于指挥所有子元素的整体排列。我们将逐一深入讲解。

1. flex-direction:定义主轴方向

此属性决定了项目排列的 方向,即主轴是水平还是垂直。

描述
row默认值。主轴为水平方向,起点在左端。
row-reverse主轴为水平方向,起点在右端。
column主轴为垂直方向,起点在上沿。
column-reverse主轴为垂直方向,起点在下沿。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    <style>
.direction-demo {
display: flex;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.direction-demo div {
width: 60px;
height: 60px;
background: #add8e6;
text-align: center;
line-height: 60px;
margin: 5px;
}
</style>

<strong>flex-direction: row (默认)</strong>
<div class="direction-demo" style="flex-direction: row;">
<div>1</div><div>2</div><div>3</div>
</div>
<strong>flex-direction: row-reverse</strong>
<div class="direction-demo" style="flex-direction: row-reverse;">
<div>1</div><div>2</div><div>3</div>
</div>
<strong>flex-direction: column</strong>
<div class="direction-demo" style="flex-direction: column;">
<div>1</div><div>2</div><div>3</div>
</div>
<strong>flex-direction: column-reverse</strong>
<div class="direction-demo" style="flex-direction: column-reverse;">
<div>1</div><div>2</div><div>3</div>
</div>

2. flex-wrap:定义是否换行

此属性决定了当一条轴线排不下所有项目时,项目是否换行。

描述
nowrap默认值。不换行,所有项目会被压缩以适应单行。
wrap换行,第一个项目在新行的上方。
wrap-reverse换行,第一个项目在新行的下方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    <style>
.wrap-demo { display: flex; width: 220px; border: 1px solid #ccc; margin-bottom: 10px; }
.wrap-demo div { width: 80px; height: 80px; background: #b0e0e6; margin: 5px; text-align: center; line-height: 80px; }
</style>
<strong>flex-wrap: nowrap (默认, 项目被压缩)</strong>
<div class="wrap-demo">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<strong>flex-wrap: wrap</strong>
<div class="wrap-demo" style="flex-wrap: wrap;">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<strong>flex-wrap: wrap-reverse</strong>
<div class="wrap-demo" style="flex-wrap: wrap-reverse;">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

3. justify-content:定义主轴对齐方式

这是最常用的容器属性之一,它定义了项目在 主轴 上的对齐和分布方式。

描述
flex-start默认值。项目向主轴起点对齐。
flex-end项目向主轴终点对齐。
center项目在主轴上居中。
space-between两端对齐,项目之间的间隔都相等。
space-around每个项目两侧的间隔相等。项目之间的间隔比项目与边框的间隔大一倍。
space-evenly所有间隔完全相等,包括项目与边框之间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <style>
.jc-demo { display: flex; height: 80px; border: 1px solid #ccc; margin-bottom: 10px; }
.jc-demo div { width: 60px; background: #87ceeb; text-align: center; line-height: 80px; }
</style>

<strong>flex-start (默认):主轴向前对齐</strong>
<div class="jc-demo" style="justify-content: flex-start;"><div>1</div><div>2</div><div>3</div></div>
<strong>flex-end:主轴向后对齐</strong>
<div class="jc-demo" style="justify-content: flex-end;"><div>1</div><div>2</div><div>3</div></div>
<strong>center:主轴居中</strong>
<div class="jc-demo" style="justify-content: center;"><div>1</div><div>2</div><div>3</div></div>
<strong>space-between:主轴两端对齐</strong>
<div class="jc-demo" style="justify-content: space-between;"><div>1</div><div>2</div><div>3</div></div>
<strong>space-around:主轴两端对齐,项目间距相等</strong>
<div class="jc-demo" style="justify-content: space-around;"><div>1</div><div>2</div><div>3</div></div>
<strong>space-evenly:主轴两端对齐,项目间距相等</strong>
<div class="jc-demo" style="justify-content: space-evenly;"><div>1</div><div>2</div><div>3</div></div>

4. align-items:定义交叉轴对齐方式 (单行)

它定义了项目在 交叉轴(默认是垂直方向)上的对齐方式。

描述
stretch默认值。如果项目未设置高度或设为 auto,将占满整个容器的高度。
flex-start交叉轴的起点对齐。
flex-end交叉轴的终点对齐。
center交叉轴的中点对齐。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.ai-demo { display: flex; height: 500px; border: 1px solid #ccc; margin-bottom: 10px; }
.ai-demo div { width: 60px; background: #b0e0e6; text-align: center; line-height: 150px; }
</style>
</head>

<body>
<!-- 可以在这里调整属性去观察他们的区别 -->
<div class="ai-demo" style="align-items: flex-end;">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>

</html>

5. align-content:定义多行内容的对齐方式

注意: 此属性仅在项目 换行 后(即 flex-wrap: wrap; 且有多行内容时)才生效。它控制的是 多根轴线 作为一个整体,在交叉轴上的对齐方式。

描述
stretch默认值。轴线占满整个交叉轴。
flex-start与交叉轴的起点对齐。
flex-end与交叉轴的终点对齐。
center与交叉轴的中点对齐。
space-between与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around每根轴线两侧的间隔都相等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style>
.ac-demo {
display: flex;
flex-wrap: wrap; /* 必须换行才能生效 */
height: 300px;
width: 250px;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.ac-demo div {
width: 60px;
height: 60px;
background: #f0f8ff;
margin: 5px;
text-align:center;
line-height:60px;
}
</style>
<strong>align-content: center (可修改查看其它效果)</strong>
<!-- 尝试修改align-content的值去观察他们的区别 -->
<div class="ac-demo" style="align-content: flex-end;">
<div>1</div><div>2</div><div>3</div>
<div>4</div><div>5</div><div>6</div>
<div>7</div><div>8</div><div>9</div>
</div>

6. flex-flow:方向与换行的简写

flex-flowflex-directionflex-wrap 两个属性的简写形式,用的很少,知道就可以了

1
2
3
4
.container {
/* 等同于 flex-direction: row; flex-wrap: wrap; */
flex-flow: row wrap;
}

6.1.3. 项目属性 (Flex Item)

这些属性写在 子元素(项目) 上,用于定义单个项目的特殊行为。

属性核心描述
order改变项目排列顺序 (数值越小越靠前)
flex-grow定义项目的放大比例 (分配剩余空间)
flex-shrink定义项目的缩小比例 (处理空间不足)
flex-basis定义项目的基础尺寸
flex(核心) 上面三者的简写,如 flex: 1 1 0,代表着
align-self允许单个项目覆盖容器的 align-items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.flex-container-item { display: flex; align-items: flex-start; height: 150px; border: 1px solid #ccc; }
.item-prop { padding: 10px; border: 1px solid #999; }
.item-p1 { background-color: #fbe9e7; order: 2; /* 排在后面 */ }
.item-p2 { background-color: #e3f2fd; flex-grow: 1; /* 占据所有剩余空间 */ }
.item-p3 { background-color: #e8f5e9; align-self: center; /* 自己居中对齐 */ }
.item-p4 { background-color: #f9fbe7; order: -1; /* 排在最前面 */ }
</style>
<div class="flex-container-item">
<div class="item-prop item-p1">Item 1 (order: 2)</div>
<div class="item-prop item-p2">Item 2 (flex-grow: 1)</div>
<div class="item-prop item-p3">Item 3 (align-self: center)</div>
<div class="item-prop item-p4">Item 4 (order: -1)</div>
</div>

6.1.4. 实战:通过游戏学习布局

任务: 通关 24 关 flexboxfroggy,通过我们学习的知识

1
2
3
4
flex-wrap:wrap-reverse;
align-content:space-between;
flex-direction:column-reverse;
justify-content:center;

6.2. Grid 网格布局:二维平面的上帝视角

如果说 Flexbox 是一维的专家,那么 Grid 就是二维布局的上帝。它将容器划分为行和列,允许我们将项目精确地放置在任意单元格中,是构建整个页面宏观布局的终极方案。

6.2.1. 核心概念

image-20250825214315628

  • Grid 容器 (grid container): 应用 display: grid 的元素。
  • Grid 项目 (grid item): 容器的直接子元素``。
  • Grid 线 (grid line): 划分网格的水平和垂直线。
  • Grid 轨道 (grid track): 两条相邻网格线之间的空间(即一行或一列)。
  • Grid 单元格 (grid cell): 四条网格线包围的最小单位。
  • Grid 区域 (grid area): 一个或多个单元格组成的矩形区域。

6.2.2. 容器属性

这些属性写在父元素上,用于定义整个网格的结构和对齐方式。

1.定义网格轨道

  • grid-template-columns
  • grid-template-rows

这两个核心属性分别用于定义网格的列和行。

描述
<length>使用 px, em 等固定单位。
<percentage>使用相对于容器尺寸的百分比。
fr(核心) "分数"单位,用于按比例分配可用空间。
repeat()用于创建重复的轨道模式,如 repeat(3, 1fr) 表示创建3个等宽的列。
minmax()定义一个尺寸范围,如 minmax(100px, 1fr) 表示最小100px,最大占一份。
auto由浏览器根据内容决定尺寸。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.grid-template-demo {
display: grid;
border: 1px solid #ccc;
/* 定义三列:第一列 100px,第二列 auto,第三列占剩余空间的 1 份 */
grid-template-columns: 100px auto 1fr;
/* 定义两行:都占剩余空间的 1 份 */
grid-template-rows: repeat(2, 1fr);
gap: 10px;
}

.grid-template-demo div {
background: #f0f8ff;
border: 1px solid #333;
text-align: center;
padding: 20px 0;
}
</style>
</head>

<body>
<div class="grid-template-demo">
<div>Row 1, Col 1<br>100px</div>
<div>Row 1, Col 2<br>auto</div>
<div>Row 1, Col 3<br>1fr</div>
<div>Row 2, Col 1<br>100px</div>
<div>Row 2, Col 2<br>auto</div>
<div>Row 2, Col 3<br>1fr</div>
</div>
</body>

</html>

2.定义网格区域

  • grid-template-areas

这是一个极具表现力的属性,允许你通过“画出”区域名称来定义布局。

描述
字符串每个字符串代表一行,字符串内的名称定义了该行的列区域。
.一个点号 (.) 代表一个空的单元格。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.areas-demo {
display: grid;
height: 300px;
/* 定义四列 */
grid-template-columns: repeat(4, 1fr);
/* 定义三行 其中第一行占100px 第二行占auto 第三行占100px */
grid-template-rows: 100px auto 100px;
gap: 10px;
/* "画出"布局 */
grid-template-areas:
"header header header header"
"sidebar main main ."
"footer footer footer footer";
}
.areas-demo div { display: grid; place-items: center; }
.area-header { grid-area: header; background-color: #add8e6; }
.area-sidebar { grid-area: sidebar; background-color: #b0e0e6; }
.area-main { grid-area: main; background-color: #e0ffff; }
.area-footer { grid-area: footer; background-color: #add8e6; }
</style>
</head>

<body>
<div class="areas-demo">
<div class="area-header">Header</div>
<div class="area-sidebar">Sidebar</div>
<div class="area-main">Main</div>
<div class="area-footer">Footer</div>
</div>
</body>

</html>

grid-area 是一个功能非常强大的属性,它既可以用于将项目放置到命名区域,也可以作为基于网格线定位的终极简写形式。

方式一:分配到命名区域
这是 grid-areagrid-template-areas 配合使用的场景,用法非常直观,也如上述示例一样

方式二:作为网格线定位的简写
grid-area 也是 grid-row-start / grid-column-start / grid-row-end / grid-column-end 四个属性的简写。

语法: grid-area: <row-start> / <col-start> / <row-end> / <col-end>;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.lines-shorthand-demo {
display: grid;
background: #f0f0f0;
border: 3px dashed #ccc;
height: 300px;
/* 创建一个五行五列的网格 */
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(5, 1fr);
gap: 5px;
}

.lines-shorthand-demo div {
display: grid;
place-items: center;
background-color: #ffcdd2;
}

.highlight-item {
/* grid-row-start: 2;
grid-column-start: 2;
grid-row-end: 5;
grid-column-end: 5;
的简写形式
*/
/* 表示我期望他从第二行开始第二列开始 */
/* 第五行第五列结束,要注意,这里的五列其实是不包含的,如果要真实到五列需要吧值改为6 */
grid-area: 2 / 2 / 5 / 5;
background-color: #c8e6c9;
color: #1b5e20;
font-weight: bold;
}
</style>
</head>

<body>
<!-- 这也是类似于百度新闻那种类似的布局,可以采用如下的方法快速创建你需要的区域 -->
<div class="lines-shorthand-demo">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="highlight-item">Highlight Area</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>

</html>

3. gap:定义网格间距

用于设置行与行、列与列之间的间距。

描述
gap: <row-gap> <column-gap>;分别设置行间距和列间距。
gap: <value>;行间距和列间距都设为同一个值。

4. 单元格内容对齐

这两个属性用于控制项目内容在其所在的单元格内部的对齐方式。可以把单元格想象成一个小房间,而项目内容(文字、图片等)就是房间里的家具,*-items 决定了家具如何在这个房间里摆放。

属性核心描述
justify-items单元格内容的水平对齐
align-items单元格内容的垂直对齐
  • start: 对齐到单元格的起始边(左/上)。
  • end: 对齐到单元格的结束边(右/下)。
  • center: 在单元格内居中。
  • stretch: (默认值) 将项目内容拉伸,填满整个单元格空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.items-align-demo {
display: grid;
height: 400px;
width: 400px;
border: 3px dashed #ccc;
background: #f0f0f0;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 10px;
border: 1px solid #ccc;
}

.items-align-demo div {
display: grid;
background-color: #e0e0e0;
padding: 10px;

/* 尝试修改这两个值来观察所有单元格内容的变化 */
justify-items: center;
/* 水平居中 */
align-items: center;
/* 垂直底部对齐 */
/* 他有一个简写属性,如下: */
/* place-items: center; */
}
</style>
</head>

<body>
<div class="items-align-demo">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
</body>

</html>

5. 网格整体对齐

核心前提:这两个属性只有在网格的总尺寸小于其容器尺寸时才会生效。它们控制的是整个网格(所有单元格作为一个整体),在容器这个“大画框”里的对齐和分布方式。

属性核心描述
justify-content整个网格作为一个整体的水平对齐与分布
align-content整个网格作为一个整体的垂直对齐与分布
  • start: 整体网格靠向容器的起始边(左/上)。
  • end: 整体网格靠向容器的结束边(右/下)。
  • center: 整体网格在容器内居中。
  • space-between / space-around / space-evenly: 控制网格轨道之间的分布方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.content-align-demo {
display: grid;
/* 容器尺寸大于网格总尺寸,创造出可用空间 */
height: 400px;
width: 400px;
background: #f0f0f0;
border: 3px dashed #ccc;

/* 网格的轨道总尺寸需要小于容器 */
/* 这里相当于设置网格的轨道总尺寸为200px */
grid-template-columns: 100px 100px;
grid-template-rows: 100px 100px;

gap: 10px;
/* 尝试修改这两个值来观察整个网格的变化 */
justify-content: start;
align-content: start;
}

.content-align-demo div {
background-color: #c8e6c9;
display: grid;
/* 让内容在网格中居中 */
/* 这是一个简写属性,相当于设置justify-items和align-items */
place-items: center;
}
</style>
</head>

<body>
<div class="content-align-demo">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>

</html>

6.2.3. 项目属性 (Grid Item)

这些属性写在**子元素(项目)**上,用于定义单个项目在网格中的位置和行为。

1. 基于网格线定位

通过指定项目从哪条网格线开始、到哪条网格线结束,来放置项目。

属性描述
grid-column-start / end定义项目占据的列范围。
grid-row-start / end定义项目占据的行范围。
grid-column / grid-row上述属性的简写,如 1 / 31 / span 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.lines-demo {
display: grid;
background: #f0f0f0;
border: 3px dashed #ccc;
/* 创建一个4列2行的网格 */
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(2, 100px);
gap: 10px;
}

.lines-demo div {
display: grid;
place-items: center;
}

.item-a {
/* 从第2条列线开始,跨越2列 */
grid-column: 2 / span 2;
/* 从第1条行线开始,到第3条行线结束 */
grid-row: 1 / 3;
background-color: #ffcdd2;
}

.item-b {
background-color: #c8e6c9;
}

.item-c {
background-color: #bbdefb;
}

.item-d {
/* 例如我想让item-d 去到第四列的第二行,那么我需要这样写: */
grid-column: 4;
grid-row: 2;
background-color: #ffcdd2;
}
</style>
</head>

<body>
<div class="lines-demo">
<div class="item-a">A</div>
<div class="item-b">B</div>
<div class="item-c">C</div>
<div class="item-d">D</div>
</div>
</body>

</html>

2. justify-selfalign-self

允许单个项目覆盖容器定义的 justify-itemsalign-items,实现独立对齐。

属性描述
justify-self单个项目内容的水平对齐
align-self单个项目内容的垂直对齐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.self-demo {
display: grid;
height: 400px;
width: 400px;
border: 3px dashed #ccc;
background: #f0f0f0;
/* 创建一个3列1行的网格 */
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 100px;
gap: 10px;
/* 容器统一垂直居中 */
}

.self-demo div {
background-color: #f3e5f5;
padding: 10px;
}

.special-item {
align-self: center;
/* 我自己要底部对齐 */
}
</style>
</head>

<body>
<div class="self-demo">
<div>Item 1</div>
<div class="special-item">Special Item</div>
<div>Item 3</div>
</div>
</body>

</html>

6.2.4. 实战:通过游戏学习布局

任务: 通关 28 关 Grid Garden,通过我们学习的知识,将萝卜种到它们该去的地方。


6.3. Position 定位:脱离常规的精准微操

摘要: 本节我们将学习一种全新的布局维度——定位 (Positioning)。您将学习如何使用 position 属性,让元素“脱离”常规的文档流,实现覆盖、固定、粘滞等高级效果。这是实现模态框、下拉菜单、固定导航栏等复杂 UI 组件的必备知识。

6.3.1. 核心前提:理解文档流

在学习定位之前,我们必须先清晰地理解什么是“常规文档流”。在默认状态下,页面元素会遵循一套既定的排列规则:

  • 块级元素 (Block): 从上到下垂直排列,每个元素独占一行。
  • 行内元素 (Inline): 在水平方向上从左到右依次排列,遇到行尾会自动换行。

这套规则就是文档流。而 position 属性,就是赋予我们打破这套常规规则,让元素“飞”出常规队列的能力。

1
2
3
4
5
6
7
8
9
10
11
12
<style>
.flow-box { border: 1px solid #ccc; padding: 10px; margin: 10px; }
.flow-box div { background-color: #e3f2fd; border: 1px solid #90caf9; padding: 10px; margin-bottom: 5px; }
.flow-box span { background-color: #fbe9e7; border: 1px solid #ffab91; padding: 5px; }
</style>
<div class="flow-box">
<div>块级元素 1</div>
<div>块级元素 2</div>
<span>行内元素 A</span>
<span>行内元素 B</span>
<span>行内元素 C</span>
</div>

6.3.2. 定位五模式核心对比

position 属性共有五个值,下表可以帮助我们从宏观上快速把握它们的核心区别。

模式脱离文档流定位参照物核心应用场景
static默认状态,无定位
relative否 (仍占原始空间)自身原始位置作为 absolute 的参照物
absolute最近的非 static 祖先弹窗、角标、覆盖层
fixed浏览器视口固定导航、返回顶部
sticky滚动到阈值后最近的滚动父级吸顶导航、表格标题

6.3.3. 核心模式详解与实战

相对定位 (relative)

相对定位非常特殊,它遵循“身在曹营心在汉”的原则:

  1. 它在文档流中依然占据着原来的空间,不会影响其他元素的布局。
  2. 它可以使用 top, right, bottom, left 属性,相对于自己的原始位置进行偏移。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style>
.relative-demo div { width: 100px; height: 100px; display: grid; place-items: center; color: white; }
.box-a { background-color: #a5d6a7; }
.box-b {
background-color: #ef9a9a;
position: relative; /* 启用相对定位 */
top: 30px; /* 向下偏移 30px */
left: 30px; /* 向右偏移 30px */
}
.box-c { background-color: #90caf9; }
</style>
<div class="relative-demo">
<div class="box-a">Box A</div>
<div class="box-b">Box B (Relative)</div>
<div class="box-c">Box C</div>
</div>

绝对定位 (absolute) 与 “父相子绝”

这是定位中最核心、最重要的应用模式。

  • position: absolute; 会让元素完全脱离文档流
  • 它会向上寻找所有祖先元素,以第一个 position 值不为 static 的祖先元素作为自己的定位参照物。
  • 如果找不到这样的祖先,它最终会相对于 <body> 定位。

为了将一个绝对定位元素限制在其直接父元素内部,我们诞生了黄金组合——“父相子绝”:

  • 父级: position: relative; (让父级成为定位参照物,但自身不脱离文档流)
  • 子级: position: absolute; (让子级脱离文档流,并相对于父级定位)

实战: 在卡片右上角添加“Hot”徽章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        <style>
.card {
width: 250px; border: 1px solid #ccc; border-radius: 8px;
font-family: sans-serif; margin: 50px auto;
/* 1. 父元素设为相对定位,成为定位参照物 */
position: relative;
}

.card img {
width: 100%;
/* 默认img是inline-block级别,但我们不希望他可以在一行内共存
而是融入到我们的父元素card容器中以单独行做排列 */
display: block; border-radius: 8px 8px 0 0;
}

.card-body {
padding: 15px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.badge {
/* 2. 子元素设为绝对定位,脱离文档流 */
position: absolute;
/* 3. 相对于 .card 的边缘进行定位 */
top: -10px; right: -10px; background-color: #e74c3c;
color: white; padding: 5px 10px; border-radius: 5px;
font-size: 14px;
}
</style>
<div class="card">
<img src="https://picsum.photos/seed/phone/400/250" alt="Product Image">
<div class="card-body">
<h4>产品标题</h4>
<p>这是一段产品描述文字。</p>
</div>
<div class="badge">Hot</div>
</div>

固定定位 (fixed)

position: fixed; 会让元素脱离文档流,并相对于浏览器视口进行定位。这意味着即使用户滚动页面,该元素的位置也永远不会改变。

实战: “返回顶部”按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html {
/* 开启平滑向上的关键属性 */
scroll-behavior: smooth;
}

h1 {
height: 1000px;
background-color: #2980b9;
color: white;
display: grid;
place-items: center;
}

.placeholder {
height: 2000px;
}

.backTop {
position: fixed;
/* 相对于视口固定 */
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
background-color: #2980b9;
color: white;
display: grid;
place-items: center;
border-radius: 50%;
text-decoration: none;
}

</style>
</head>

<body>
<h1>顶部区域</h1>
<div class="placeholder"></div>
<a href="#" class="backTop"></a>
</body>

</html>

6.3.4. 综合实战:创建一个居中模态框 (Modal)

任务: 综合运用 position: fixedz-index,创建一个覆盖全屏的半透明遮罩层,并在其上居中显示一个模态框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.modal-overlay {
position: fixed;
/* 固定定位,覆盖整个视口 */
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
/* 半透明黑色遮罩 */
z-index: 1000;
/* 确保在最顶层 */

/* 使用 Flexbox 实现完美的垂直水平居中 */
display: flex;
justify-content: center;
align-items: center;
}

.modal-content {
background-color: white;
padding: 30px;
border-radius: 8px;
width: 80%;
max-width: 500px;
text-align: center;
}


.modal-overlay {
display: none;
}
</style>
</head>

<body>

<button class="open-modal">打开模态框</button>
<div class="modal-overlay">
<div class="modal-content">
<h3>这是一个模态框</h3>
<p>这里是模态框的内容。遮罩层使用了 position: fixed,模态框本身通过 Flexbox 在遮罩层内居中。</p>
<button>关闭</button>
</div>
</div>
<script>
// 简单的写几行Js代码来打开我们的模态框
const openModal = document.querySelector('.open-modal');
const modalOverlay = document.querySelector('.modal-overlay');
openModal.addEventListener('click', () => {
modalOverlay.style.display = 'flex';
});
</script>
</body>

</html>

6.4. 多列布局 (Multi-column):优雅实现内容分栏与瀑布流

摘要: 本节我们将学习一个巧妙而高效的布局工具——多列布局。您将学习如何像排版报纸和杂志一样,轻松地将大段文本内容分割成多列以提升可读性。更重要的是,我们将通过一个实战案例,向您展示如何仅用三行核心 CSS 代码就实现一个完美的、响应式的图片瀑布流布局,这是一个在现代网页设计中非常流行且实用的技巧。

6.4.1. 核心属性详解

多列布局的所有属性都应用于容器元素,它会将容器内的所有子元素(无论块级还是行内)自动分配到各列中。

属性核心描述
column-count固定列的数量。
column-width定义每列的理想宽度,浏览器会据此自动调整列数,实现响应式。
columnscountwidth 的简写属性。
column-gap定义列与列之间的间距。
column-rule定义列与列之间的分隔线样式,语法与 border 类似。
column-span:all让标题横跨所有列
break-inside:avoid避免在元素内部断开换行,让元素尽量完整显示在同一列。

深入讲解 column-countcolumn-width

这两个属性是多列布局的核心,通常我们只使用其中一个来驱动布局:

  • column-count (固定列数): 当你希望布局始终保持特定数量的列(例如,一个三栏文章),无论容器宽度如何变化时,使用此属性。
  • column-width (自适应列数): 当你希望布局响应式地变化时,使用此属性。你只需定义一个“理想”的列宽(例如 200px),浏览器会自动计算在当前容器宽度下,能容纳多少个这样的列。当容器变宽,列数会自动增加;容器变窄,列数会自动减少。这是实现响应式瀑布流的关键。

6.4.2. 实战应用

实战一:报纸式文章排版

任务: 将一段长文本自动格式化为三列,并在列与列之间添加一条浅灰色分隔线,同时让文章标题横跨所有列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<style>
.newspaper-article {
width: 90%;
margin: auto;
border: 1px solid #ddd;
padding: 20px;

/* 1. 固定为 3 列 */
column-count: 3;
/* 2. 设置列间距 */
column-gap: 40px;
/* 3. 设置列间的分隔线 */
column-rule: 1px solid #eee;
}
.newspaper-article h2 {
/* 4. 让标题横跨所有列 */
column-span: all;
text-align: center;
margin-top: 0;
margin-bottom: 20px;
}
.newspaper-article p {
text-align: justify;
line-height: 1.7;
}
</style>
<article class="newspaper-article">
<h2>CSS 多列布局的艺术</h2>
<p>多列布局是一个经常被低估的现代布局工具,它可以轻松地将一段连续的内容(如文本)分割成报纸或杂志那样的多列。这种布局方式不仅美观,还能显著提高长篇文本的可读性,因为它避免了用户视线需要进行过长的水平移动。</p>
<p>要使用多列布局,你只需在容器元素上应用几个简单的 CSS 属性。最核心的属性是 column-count 和 column-width。前者用于定义固定的列数,而后者则定义一个理想的列宽,浏览器会根据容器的可用空间自动计算出合适的列数,从而轻松实现响应式效果。这使得多列布局也成为创建图片瀑布流的一种极其简单高效的方法。</p>
<p>除了基本的列数和宽度,我们还可以通过 column-gap 来控制列与列之间的间距,使用 column-rule 来在列间添加装饰性的分隔线。更有趣的是,通过 column-span 属性,我们可以让某个子元素(比如一个大标题或者一张关键图片)横跨所有列,打破单调的布局,创造出更富吸引力的视觉焦点。</p>
</article>

实战二:纯 CSS 图片瀑布流

任务: 创建一个响应式的图片瀑布流布局。图片会根据浏览器窗口宽度自动重新排列成不同的列数,并且每张图片都保持完整,不会被列分割。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.waterfall-container {
/* 1. 定义一个理想的列宽,浏览器会据此自动计算列数,实现响应式 */
column-width: 220px;
/* 2. 定义列间距 */
column-gap: 15px;
width: 90%;
margin: auto;
}

.waterfall-item {
width: 100%;
/* 图片宽度充满其所在列 */
height: auto;
/* 高度自适应 */
margin-bottom: 15px;
border-radius: 8px;
/* 3. 核心:防止图片在换列时被从中间截断 */
break-inside: avoid;
display: block;
/* 确保图片是块级元素,消除底部幽灵空白 */
}
</style>
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
</head>

<body>
<div class="waterfall-container" x-data="{
images: [
{ seed: 'fall1', height: 600 },
{ seed: 'fall2', height: 500 },
{ seed: 'fall3', height: 650 },
{ seed: 'fall4', height: 450 },
{ seed: 'fall5', height: 700 },
{ seed: 'fall6', height: 520 },
{ seed: 'fall7', height: 580 },
{ seed: 'fall8', height: 480 },
{ seed: 'fall9', height: 620 },
{ seed: 'fall10', height: 540 },
{ seed: 'fall11', height: 680 },
{ seed: 'fall12', height: 420 },
{ seed: 'fall13', height: 750 },
{ seed: 'fall14', height: 460 },
{ seed: 'fall15', height: 590 },
{ seed: 'fall16', height: 510 },
{ seed: 'fall17', height: 640 },
{ seed: 'fall18', height: 470 },
{ seed: 'fall19', height: 720 },
{ seed: 'fall20', height: 550 },
{ seed: 'fall21', height: 610 },
{ seed: 'fall22', height: 490 },
{ seed: 'fall23', height: 660 },
{ seed: 'fall24', height: 430 }
]
}">
<template x-for="image in images" :key="image.seed">
<img class="waterfall-item"
:src="`https://picsum.photos/seed/${image.seed}/400/${image.height}`">
</template>
</div>
</body>

</html>

请尝试在此将代码复制到Vs Code中,启用 F12 浏览器 拉动浏览器窗口查看效果


6.5. 响应式设计:让布局适应万千屏幕

摘要: 本节我们将学习现代网页开发的基石——响应式设计。这并非一种新的布局技术,而是一种设计思想与方法论。我们将学习其三大核心支柱:流式网格、弹性图片和媒体查询。通过掌握这些,您将能够让一套代码在手机、平板和桌面电脑上呈现出截然不同却都同样完美的布局效果,为所有用户提供最佳的浏览体验。

6.5.1. 核心概念与基石

响应式设计的实现依赖于几项关键的 CSS 技术和开发思想。

视口 (Viewport) 元标签

这是开启移动端适配的必备第一步,是响应式设计的“准生证”。这行代码必须被添加到 HTML 文件的 <head> 标签中。

1
<meta name="viewport" content="width=device-width, initial-scale=1.0">

它告诉移动端浏览器:

  • width=device-width: 将视口的宽度设置为设备的屏幕宽度。
  • initial-scale=1.0: 页面的初始缩放比例为 1.0,即不缩放。

如果没有这行代码,移动端浏览器会默认使用一个桌面宽度的“虚拟视口”(通常是 980px)来渲染页面,然后将整个页面缩小,导致文字变得极小,布局混乱。

媒体查询 (Media Queries)

媒体查询是响应式设计的技术核心,它像一个 CSS 的 if 语句,允许我们为满足特定条件的设备应用特定的样式。

语法: @media (媒体特性) { /* 在此条件满足时应用的 CSS 规则 */ }

常用媒体特性描述示例
min-width当视口宽度 大于或等于 指定值时应用样式。@media (min-width: 768px)
max-width当视口宽度 小于或等于 指定值时应用样式。@media (max-width: 767px)
orientationportrait (竖屏) 或 landscape (横屏)。@media (orientation: landscape)

设计思想:移动优先

这是一种重要的现代响应式设计策略。我们不再先为大屏幕设计,再去“修补”小屏幕的样式。而是反过来:

  1. 先编写基础样式: 这些样式服务于最小的设备(手机),保证核心功能和内容的完美呈现。这些代码写在所有 @media 规则之外。
  2. 逐步增强: 使用 min-width 媒体查询,为更大的屏幕(如平板、桌面)追加更复杂的样式和布局。
    “移动优先”的优势:
  • 性能更佳: 移动设备只需加载最核心、最轻量的 CSS。
  • 代码更清晰: 避免了在媒体查询中大量重置和覆盖桌面端样式,代码逻辑是不断“增加”而非“修改”。
  • 体验更专注: 强迫我们优先考虑移动端用户的核心体验。

6.5.2. 断点的选择

“断点”是指布局发生显著变化的屏幕宽度临界点。选择哪些断点并没有绝对的标准,最佳实践是根据你的内容来决定在何时需要改变布局。不过,业界通常会参考一些主流 UI 框架(如 Bootstrap)的约定,这有助于团队协作。

一套常用的断点参考:

设备类型断点描述
手机 (Mobile)< 768px默认样式,通常为单列布局。
平板 (Tablet)≥ 768px布局开始变为多列,空间更宽裕。
桌面 (Desktop)≥ 992px经典的桌面端宽屏布局。
大桌面 (Large Desktop)≥ 1200px为超宽屏幕提供更丰富的布局。

6.5.3. 实战:将 Grid 首页布局改造为响应式

任务: 我们将以 6.2.3 节中创建的经典网站布局为基础,采用“移动优先”的策略,为其添加响应式能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式 Grid 布局</title>
<style>
body { margin: 0; font-family: sans-serif; }
.responsive-layout {
display: grid;
min-height: 100vh;
gap: 10px;

/* 默认样式 (移动优先):
这是为小于 768px 的屏幕准备的单列布局。
它在所有设备上都会生效,除非被后续的媒体查询覆盖。
*/
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
grid-template-columns: 1fr;
grid-template-rows: 60px 1fr auto 60px;
}

/* 断点 1:平板及以上设备
当屏幕宽度大于等于 768px 时,应用以下规则,
覆盖掉默认的移动端布局。
*/
@media (min-width: 768px) {
.responsive-layout {
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 200px 1fr;
}
}

/* 以下是各区域的样式,无需改变 */
.r-header { grid-area: header; background-color: #add8e6; }
.r-sidebar { grid-area: sidebar; background-color: #b0e0e6; }
.r-main { grid-area: main; background-color: #f0f8ff; }
.r-footer { grid-area: footer; background-color: #add8e6; }
.r-header, .r-sidebar, .r-main, .r-footer { display: grid; place-items: center; font-size: 20px;}
</style>
</head>
<body>
<div class="responsive-layout">
<header class="r-header">Header</header>
<aside class="r-sidebar">Sidebar</aside>
<main class="r-main">Main Content</main>
<footer class="r-footer">Footer</footer>
</div>
</body>
</html>

请尝试在此将代码复制到Vs Code中,启用 F12 浏览器 拉动浏览器窗口查看效果


6.6. 最后的拼图:理解 Float

摘要: 在本章的最后,我们将回顾一个在 CSS 历史上扮演过重要角色、但如今已回归其本职的属性——float。本节的目标不是让您用它来构建复杂的布局,恰恰相反,是让您理解它的唯一正确应用场景(图文环绕),并能看懂和维护那些使用浮动布局的“历史遗留”代码。我们将重点讲解浮动带来的“高度塌陷”问题以及经典的“清除浮动”解决方案。

6.6.1. Float 的唯一正确用途:图文环绕

float 属性最初被设计出来的唯一目的,就是为了实现类似报纸、杂志中的图文环绕排版效果。通过设置 float: left;float: right;,可以让一个元素(通常是图片)脱离正常的文档流,移动到其容器的左侧或右侧,其后的文本内容则会环绕在该元素的周围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
.article-content {
width: 80%;
margin: auto;
border: 1px solid #ccc;
padding: 20px;
font-family: sans-serif;
line-height: 1.7;
}
.article-content img {
/* 1. 让图片向左浮动 */
float: left;
/* 2. 给图片右侧和下方一些间距,避免文字紧贴 */
margin-right: 20px;
margin-bottom: 10px;
border-radius: 8px;
}
</style>
<div class="article-content">
<img src="https://picsum.photos/seed/float-demo/200/200" alt="Scenery">
<p>这段文字将优雅地环绕在图片的周围。浮动(Float)最初就是为了实现这种类似报纸和杂志的图文混排效果而设计的。在现代 CSS 布局中,除了这个场景,我们几乎不再需要使用它。当一个元素浮动后,它会部分地脱离文档流,允许其他行内内容(如文本)围绕它流动。这是它最核心、也是如今唯一推荐的用途。</p>
<p>然而,开发者们曾“滥用”浮动来实现多列布局,但这会带来一系列的副作用,其中最著名的就是“高度塌陷”问题,我们将在下一节探讨它。</p>
</div>

6.6.2. 历史遗留问题与解决方案:清除浮动

**问题:高度塌陷 **

痛点背景:当一个父元素内部的所有子元素都设置了浮动时,这些子元素会完全脱离常规文档流。对于父元素来说,它会认为自己“内部没有任何内容”,因此它的高度就会“塌陷”为 0。这将彻底打乱后续元素的布局。

直观演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style>
.parent-box {
border: 3px solid #e74c3c;
width: 100%;
}
.parent-box .child {
float: left;
width: 100px;
height: 100px;
background: #fbe9e7;
margin: 10px;
}
.next-element {
margin-top: 20px;
background: #e3f2fd;
padding: 10px;
}
</style>
<div class="parent-box">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
</div>
<div class="next-element">
我是父元素后面的元素。看,父元素的红色边框塌陷了,我的位置也乱了。
</div>

解决方案:清除浮动

为了解决高度塌陷问题,开发者们创造了多种“清除浮动”的方法。在 2025 年的今天,最常用且最标准的方法是 Clearfix Hack

它的原理是:利用 ::after 伪元素,在父元素的内容之后添加一个看不见的、块级的、并且设置了 clear: both; 的虚拟元素。clear: both; 这条规则会命令这个虚拟元素“移动到所有浮动元素的下方”,从而迫使父元素必须将高度延伸到这个虚拟元素所在的位置,也就奇迹般地“撑开”了父元素。

标准 Clearfix 代码:

1
2
3
4
5
6
7
.clearfix::after {
content: ""; /* 必须有 content 属性 */
display: block; /* 必须是块级元素 */
clear: both; /* 核心:清除两侧的浮动 */
height: 0; /* 确保它不产生额外高度 */
visibility: hidden; /* 让它不可见 */
}

修复后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<style>
/* 引入 Clearfix 解决方案 */
.clearfix::after {
content: "";
display: block;
clear: both;
}
.parent-box-fixed {
border: 3px solid #2ecc71;
width: 100%;
}
.parent-box-fixed .child {
float: left;
width: 100px;
height: 100px;
background: #e8f5e9;
margin: 10px;
}
.next-element-fixed {
margin-top: 20px;
background: #e3f2fd;
padding: 10px;
}
</style>
<div class="parent-box-fixed clearfix">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
</div>
<div class="next-element-fixed">
现在父元素的绿色边框正常了,我的位置也正确了。
</div>

现代视角: Clearfix 的整个概念虽然巧妙,但也体现了使用 float 进行布局的复杂性和脆弱性。这正是我们现在拥抱 FlexboxGrid 的根本原因——它们从设计上就避免了这类问题,让布局变得简单、直观和健壮。