浮动、定位、Flex 解决的是同一类问题——把元素摆到想要的位置,只是属于不同年代的方案。放在一起对比,比分开学更容易记住各自适合什么场景。

一、浮动 float

demo 9:CSS 浮动

float 最初的用途是实现”文字环绕图片”的效果(就像杂志排版),后来被大量拿来搭多列布局。它的取值很简单:

1
2
3
4
5
6
7
8
9
10
11
12
.left-son {
width: 100px;
height: 100px;
background-color: yellow;
float: left; /* 向左浮动 */
}
.right-son {
width: 100px;
height: 100px;
background-color: orange;
float: right; /* 向右浮动 */
}

浮动的元素会尽量靠向指定方向,多个同向浮动元素会依次紧贴排列,一行放不下就换行。

半脱离文档流

浮动元素会”浮”起来:它原来占据的位置会让给后面的元素,但行内内容(文字)会绕着它排列。这就是”半脱离”——和 position: absolute 的完全脱离不一样。

父元素高度坍塌

这是浮动最经典的坑:

1
2
3
4
5
6
7
8
9
10
11
12
13
.father {
background-color: blueviolet;
height: 150px; /* 这个高度是手动写死的 */
border: 5px solid black;
/* overflow: hidden; */ /* 清除浮动方案一 */
}

/* 清除浮动方案二(推荐)
.father::after {
content: "";
display: table;
clear: both;
} */

如果把 height: 150px 去掉,父元素的高度会变成 0 ——因为浮动的子元素不再参与父元素的高度计算。父元素的背景色、边框都会缩成一条线,后面本来该在下面的内容会顶上来,整个页面就乱了。

demo 里写死 height: 150px 只是临时绕过,页面内容一变高度就不对了。正规做法是清除浮动,让父元素重新”包住”浮动子元素:

方案 写法 说明
overflow: hidden 给父元素加 最省事,但会裁掉超出父元素的内容,也可能影响 position: sticky
::after 伪元素 见上面注释 推荐,不影响其他布局,兼容性也好

::after 方案的原理是:在父元素末尾”凭空”生成一个空内容,给它 clear: both,意思是”这个元素的左边和右边都不允许有浮动元素”。于是它被迫排到所有浮动元素下面,父元素的高度自然就被撑开了。

浮动的初心是文字环绕,不是布局。今天要排多列布局,请直接用 Flex 或 Grid——它们没有高度坍塌这类问题。浮动现在的主要用途就剩”文字环绕图片”了。

二、定位 position

demo 10:CSS 定位

定位的关键在于搞清楚**”我相对于谁来偏移”,以及“我原来的位置还占不占”**。

relative:相对定位

1
2
3
4
5
6
7
8
.box-relative {
width: 100px;
height: 100px;
background-color: black;
position: relative;
left: 120px; /* 向右偏移 120px */
top: 40px; /* 向下偏移 40px */
}
  • 参照物:元素自己原来的位置;
  • 原来的空间仍然占着(不脱离文档流),所以后面的元素不会补位,看起来就像元素”飘”出去了一点,留下一个洞。

因为这一点,relative 很少单独用来移动元素,更常见的用法是给父元素加 position: relative,作为子元素绝对定位的参照物。

absolute:绝对定位

1
2
3
4
5
6
7
.box-absolute {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
left: 100px;
}
  • 参照物:最近的已定位祖先元素(即祖先里第一个 position 不是 static 的)。如果所有祖先都没定位,就相对初始包含块(可以粗略理解为页面左上角);
  • 完全脱离文档流:原来的位置会被后面的元素直接瓜分——demo 里红块后面的方块会往上顶。

这就是为什么”父相子绝”是最常见的搭配:

1
2
.father { position: relative; }   /* 父:定位参照物,自身不移动 */
.son { position: absolute; top: 0; right: 0; } /* 子:贴在父元素右上角 */

fixed:固定定位

1
2
3
4
5
6
7
8
.box-fixed {
width: 100px;
height: 100px;
background-color: blue;
position: fixed;
right: 0; /* 贴着视口右边 */
top: 300px;
}
  • 参照物:浏览器视口,和滚动位置无关;
  • 完全脱离文档流。

页面滚动时它纹丝不动,所以适合做回到顶部按钮、悬浮客服、固定导航栏。

三者对比

position 参照物 是否占原位置 典型用途
relative 自己原来的位置 占 做绝对定位的参照物、微调位置
absolute 最近的已定位祖先 不占 角标、下拉菜单、弹层
fixed 浏览器视口 不占 固定导航、回到顶部

配套的偏移属性

top / right / bottom / left 用来指定偏移方向和距离。规则是:写 left 就从左边量,写 right 就从右边量,同时写 left 和 right 会拉伸宽度(如果没写 width)。

补充:还有一种 position: sticky,它是 relative 和 fixed 的结合体——在容器内滚动到指定位置前表现得像 relative,超过阈值后就”粘”在那里。本站的导航栏和侧边栏就用了类似的粘性效果。

三、Flex 弹性布局

demo 18:Flex 弹性布局

Flex 是目前最主流的布局方案。用法只有两步:给容器加 display: flex,它的直接子元素自动变成”弹性项目”,然后靠容器上的几个属性统一控制排列方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
html {
font-size: 10px; /* 让 1rem = 10px,方便算:40rem = 400px */
}

.container {
display: flex;
height: 40rem;
background-color: aqua;
justify-content: center; /* 主轴居中 */
align-items: center; /* 交叉轴居中 */
}

.item {
width: 10rem; /* 100px */
font-size: 8rem;
}

先理解主轴和交叉轴

Flex 里所有对齐属性都围绕两条轴,这是理解 Flex 的唯一门槛:

1
2
3
4
5
6
7
8
默认 flex-direction: row 时

主轴(水平)→
┌──────────────────────────┐
│ │ ↑
│ [1][2][3] │ 交叉轴
│ │ ↓
└──────────────────────────┘
  • justify-content 控制主轴上的对齐;
  • align-items 控制交叉轴上的对齐。

一旦把 flex-direction 改成 column,主轴变成垂直方向,这两个属性的作用方向也就跟着互换了——所以不要死记”justify 是水平、align 是垂直”。

容器属性速查

flex-direction —— 决定主轴方向:

值 排列效果
row 从左到右(默认)
row-reverse 从右到左
column 从上到下
column-reverse 从下到上

flex-wrap —— 一行放不下时怎么办:

值 效果
nowrap 不换行,压缩子元素(默认)
wrap 换行
wrap-reverse 换行,且行序反向

justify-content —— 主轴对齐:

值 效果
flex-start 靠主轴起点(默认)
flex-end 靠主轴终点
center 居中
space-between 两端对齐,中间平均分配
space-around 每个元素两侧等距(导致两端有半个间隙)

align-items —— 交叉轴对齐:

值 效果
stretch 拉伸填满(默认)
flex-start / flex-end 靠交叉轴起点 / 终点
center 居中
baseline 按文字基线对齐

demo 里 6 个方块被 justify-content: center + align-items: center 稳稳地摆在了正中央——你可以边看页边把这两个值改成 space-between、flex-end 试试效果。

和浮动对比一下

同样是”一排方块水平居中”,两种方案的差距:

1
2
3
4
5
6
7
8
9
10
/* 浮动方案:需要清除浮动,还得靠 margin 硬凑居中 */
.container { overflow: hidden; }
.item { float: left; }

/* Flex 方案:两行搞定,垂直居中也不用算高度 */
.container {
display: flex;
justify-content: center;
align-items: center;
}

垂直居中是最能体现差距的地方。用传统方案让一个未知高度的元素垂直居中,得动 absolute + transform 或者 table-cell 之类的技巧;Flex 只要一个 align-items: center。

选择顺序建议:能用 Flex 就用 Flex,二维网格布局(同时控制行和列)再上 Grid。浮动留给文字环绕,定位留给悬浮元素。

小结

方案 参照物 是否占位 适合场景
float — 半脱离 文字环绕;不要用来搭布局
relative 自己原位置 占 当绝对定位的参照物
absolute 最近的已定位祖先 不占 角标、下拉菜单、弹层
fixed 浏览器视口 不占 固定导航、回到顶部
flex — — 绝大多数一维布局

下一篇进入 JavaScript,从导入方式一路写到 DOM 操作和移动端 rem 适配。


本系列其他文章