CSS position 有哪些取值?relative、absolute、fixed、sticky 有什么区别?
简化版
position 控制元素定位方式。static 是默认流式布局;relative 相对自身原位置偏移且保留占位;absolute 相对最近定位祖先定位并脱离文档流;fixed 相对视口固定;sticky 在普通流和固定定位之间切换。
详细版
常见取值:
static:默认值,top/right/bottom/left不生效。relative:相对自身原位置偏移,不脱离文档流。absolute:相对最近非static祖先定位,脱离文档流。fixed:相对视口定位,脱离文档流,常用于悬浮按钮、固定头部。sticky:滚动到阈值前按普通流布局,达到阈值后像 fixed 一样粘住。
面试重点是 containing block:absolute 不一定相对父元素,而是相对最近有定位的祖先;如果没有,就相对初始包含块。工程里常给父元素加 position: relative,让子元素绝对定位有参照物。
完整版教学
一、relative 的特点
relative 会保留元素在普通文档流里的位置,然后视觉上偏移。
.badge {
position: relative;
top: -4px;
}
它常用于小范围微调,也常作为 absolute 子元素的定位上下文。注意:偏移后的元素可能覆盖别人,但原来的占位还在。
二、absolute 的参照物
absolute 会脱离普通文档流,不再占据原来的空间。它的定位参照物是最近的“定位祖先”,也就是 position 不为 static 的祖先。
.card {
position: relative;
}
.close {
position: absolute;
top: 8px;
right: 8px;
}
如果 .card 没有 position: relative,关闭按钮可能跑到页面其他参照物上,这就是很多定位 bug 的来源。
三、fixed 和 transform 的特殊问题
fixed 通常相对视口定位,但如果祖先元素设置了某些属性,比如 transform、filter、perspective,可能会创建新的包含块,让 fixed 表现得像相对这个祖先固定。
这在弹窗、抽屉、移动端页面里很常见。遇到 fixed 不按视口固定,可以检查祖先是否有 transform。
四、sticky 的生效条件
sticky 必须配合阈值,比如 top: 0。它还受滚动容器影响,粘住的是最近滚动祖先,不一定是整个窗口。
.toc {
position: sticky;
top: 80px;
}
如果 sticky 不生效,常见原因是父容器高度不够、祖先设置了不合适的 overflow、没有设置 top/bottom 阈值。
五、面试追问与工程落地
定位题经常会追问 z-index。z-index 不是一个全页面绝对数字,它只在同一个层叠上下文里比较。position 配合 z-index、opacity < 1、transform、filter 等都可能创建新的层叠上下文。一个元素 z-index: 9999 仍然可能被另一个上下文里的元素盖住。
还要注意 absolute 的包含块不只由 position 决定。某些 CSS 属性如 transform 也会影响 fixed 或 absolute 的定位参照。实际遇到弹窗位置异常、固定按钮跟着某个容器走,应该检查祖先元素是否创建了新的 containing block。
工程建议是:组件内部的小角标、关闭按钮,用父级 position: relative 加子级 absolute;全局弹窗、toast、dropdown 更推荐统一挂到顶层容器,避免被局部 overflow、transform、z-index 上下文限制。
六、用两条轴和包含块推导定位结果
面试给出定位代码时,先确定元素是否脱离普通流,再找包含块,最后分别计算 inline 轴和 block 轴的 inset。假设定位祖先 padding box 为 600 × 400px,子元素为 120 × 80px,写 left: 50%; top: 50% 时子元素左上角落在 (300, 200);再加 translate(-50%, -50%) 后各减自身一半,最终左上角为 (240, 160)。
| 值 | 是否保留普通流占位 | 常见定位参照 | 典型用途 |
|---|---|---|---|
static | 是 | 所在格式化上下文 | 普通内容 |
relative | 是 | 自身原始位置 | 微调、建立定位包含块 |
absolute | 否 | 最近的绝对定位包含块 | 角标、组件内浮层 |
fixed | 否 | 通常是视口,也可能是祖先包含块 | 全局悬浮控件 |
sticky | 是 | 最近滚动容器的滚动口并受包含块约束 | 吸顶标题、目录 |
absolute“相对最近非 static 祖先”是常用简化说法;严谨判断还要检查 transform、contain、will-change 等会建立定位包含块的属性。
七、常见误区与追问
- 误区:
absolute一定相对直接父元素定位。 它寻找最近建立相应包含块的祖先,直接父元素若未建立包含块就会继续向上查找。 - 误区:
fixed在任何情况下都只相对浏览器视口。 某些祖先的transform、contain或will-change能建立 fixed positioning containing block。 - 误区:
z-index: 999999一定能盖住所有元素。z-index先在各自层叠上下文内排序,子元素无法用大数字越过祖先所在的上下文层级。 - 追问:
relative偏移后会挤开相邻元素吗? 不会重新安排相邻盒,它保留原占位并做视觉偏移,但可能扩大祖先的可滚动溢出区域。 - 追问:
sticky什么时候开始粘住? 当滚动使盒子越过指定的非 auto inset 阈值时,它相对最近滚动口调整位置,同时仍受自身包含块边界限制。 - 追问:为什么
sticky在短父容器里看不出效果? 它不能无限越过包含块;若可滚动距离或父级可用范围不足,粘滞阶段会很短甚至不可见。 - 追问:全局弹窗为什么常用 portal 挂到页面顶层? 这样可以减少局部 overflow 裁剪、transform 包含块和层叠上下文对定位与覆盖关系的干扰。
八、加强记忆
定位可以按“是否脱离文档流”和“相对谁定位”记:relative 不脱流、相对自己;absolute 脱流、找定位祖先;fixed 脱流、通常看视口;sticky 先在流里,滚到阈值再粘住。