技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTCSS

深入理解 position

所属馆藏
CSS
文件格式
Markdown
原始路径
CSS/25-深入理解 position
本文目录10 个章节
  1. position 属性概述
  2. 定位中的几个基础概念
  3. absolute 属性值
  4. z-index 与定位的关系
  5. relative 属性值
  6. fixed 属性值
  7. sticky 属性值
  8. 五种定位方式对比
  9. 总结
  10. 参考资料

深入理解 position

Category(分类): CSS Status: 未知

position 属性概述

position 是 CSS 布局中非常重要的属性,用于指定元素参与布局和定位的方式。配合 toprightbottomleft(也可以使用逻辑属性 inset-*)可以调整元素的位置,配合 z-index 可以控制元素在同一堆叠上下文中的绘制层级。

position 常见的五个值如下:

  • static:默认值,元素按照普通流布局。toprightbottomleft 通常不生效;z-index 对普通块盒通常也不生效,但 Flex 项目和 Grid 项目是例外。
  • relative:相对定位。元素仍然保留在普通流中的原始空间,视觉位置可以相对于自身原来的位置偏移;同时可以为绝对定位后代建立包含块。
  • absolute:绝对定位。元素脱离普通流,通常相对于最近的定位祖先或其他能够建立包含块的祖先定位。
  • fixed:固定定位。通常相对于视口定位,不随页面滚动;但某些祖先的 transformfilterperspectivecontain 等属性可能改变它的包含块。
  • sticky:粘性定位。元素保留普通流空间,在滚动过程中相对于最近的滚动容器和设定的 inset 阈值进行粘滞定位,并受到包含块边界限制。

定位中的几个基础概念

普通流

普通流(Normal Flow)是浏览器默认的布局方式。块级盒通常沿块方向依次排列,行内内容在行盒中排列并在空间不足时换行。普通流中的元素会为自己占据布局空间,并影响后续元素的位置。

包含块

包含块(Containing Block)是定位元素计算尺寸和偏移的参考区域。绝对定位元素通常寻找最近的定位祖先作为包含块;position: relativeabsolutefixed 的祖先通常可以建立包含块,某些 transformfiltercontain 等属性也可能建立包含块。

如果没有合适的祖先,绝对定位元素通常会相对于初始包含块定位。实际项目中,常见写法是为需要承载角标、按钮或弹出层的容器设置:

.container {
  position: relative;
}

inset 属性和百分比

toprightbottomleft 是物理方向的偏移属性,也可以使用 inset 及其逻辑方向属性:

.overlay {
  inset: 0;
}

百分比的参考方向不能简单地都理解为宽度:

  • leftright 的百分比通常相对于包含块宽度;
  • topbottom 的百分比通常相对于包含块高度。

absolute 属性值

基本特性

绝对定位元素与浮动元素都可能脱离普通流,但两者不是同一种布局机制:

  • 浮动元素会影响周围行内内容的环绕;
  • 绝对定位元素不会为普通流保留位置,也不会让文字环绕;
  • 绝对定位元素的 float 会被计算为 none
  • 两者的宽度计算、包含块和定位规则不同。

在宽度为 auto 时,绝对定位元素有时会表现出类似“包裹内容”的收缩宽度,但这不是绝对定位永远具有的特性。如果同时设置了相对方向的 inset,例如 left: 0right: 0,元素也可能被拉伸到包含块的可用宽度。

绝对定位元素不参与普通流,因此当父元素的高度完全依赖这个子元素时,父元素可能出现高度不足或高度塌陷。但如果父元素有明确高度、其他普通流内容或其他布局约束,则不一定发生高度塌陷。

脱离普通流

position 设置为 absolute 后,元素会脱离普通流,不再为自己保留原来的布局空间。普通流中的其他元素会像它不存在一样参与布局,而绝对定位元素会在绘制阶段按照自己的定位规则显示,并可能覆盖其他内容。

<div class="item"></div>
<img src="example.jpg" alt="示例图片">
.item {
  position: absolute;
  width: 300px;
  height: 200px;
  background: #ec407a;
}

img {
  display: block;
  width: 500px;
}

上例中,.item 不再占据普通流空间,图片可以从它原来的位置开始布局;如果两者发生重叠,绝对定位元素可能覆盖图片。

元素未脱离普通流与脱离普通流的对比一

绝对定位元素覆盖普通流内容的示意图

margin 共同使用:定位下拉列表

绝对定位配合 margin 可以实现偏移,但现代项目通常更推荐使用明确的 topleft 等 inset 属性,让定位关系更加清晰。

<div class="container">
  <input class="input" placeholder="请输入内容" aria-label="搜索">
  <ul class="list">
    <li><a href="#">玩转 CSS 布局之 Grid 布局</a></li>
    <li><a href="#">玩转 CSS 布局之 Flex 布局</a></li>
    <li><a href="#">玩转 CSS 布局之深入理解 position 定位</a></li>
    <li><a href="#">玩转 CSS 布局之深入理解 z-index 定位</a></li>
  </ul>
</div>
.container {
  position: relative;
  width: 260px;
  margin: 120px auto 0;
}

.input {
  box-sizing: border-box;
  width: 260px;
  min-height: 38px;
  padding: 10px;
  border: 1px solid #d5d7d8;
}

.input:focus-visible {
  outline: 3px solid rgb(37 99 235 / 35%);
  outline-offset: 2px;
}

.list {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 10;
  display: none;
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid #e6e8e9;
  background: #fff;
  box-shadow: 0 1px 2px rgb(0 0 0 / 15%);
  font-size: 12px;
}

/* 由 JavaScript 或 :focus-within 控制显示状态 */
.container:focus-within .list {
  display: block;
}

.list a {
  display: block;
  padding: 0 12px;
  color: #5e5e5e;
  line-height: 30px;
  text-decoration: none;
}

.list a:hover,
.list a:focus-visible {
  background: #f9f9f9;
  color: #000;
}

这里必须设置 .container { position: relative; },否则 .list 可能相对于更外层的包含块定位,不能稳定地贴合搜索框。

绝对定位下拉列表示意图

toprightbottomleft 的常见用法

1. 使用 inset 创建覆盖层

如果覆盖层应该覆盖一个容器,需要让该容器建立定位包含块:

<div class="panel">
  <div class="overlay"></div>
</div>
.panel {
  position: relative;
  min-height: 240px;
}

.overlay {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0.5;
}

如果目标是覆盖视口,而不是某个容器,应使用 fixed

.viewport-overlay {
  position: fixed;
  z-index: 1000;
  inset: 0;
  background: rgb(0 0 0 / 50%);
}

2. 使用左右 inset 和自动外边距水平居中

绝对定位元素可以通过 left: 0right: 0、固定或最大宽度以及自动外边距实现水平居中:

.image {
  position: absolute;
  right: 0;
  left: 0;
  width: min(800px, calc(100% - 2rem));
  margin-inline: auto;
}

这里的 margin: auto 不是无效的,关键是需要同时设置左右 inset 和合适的宽度。实际项目还应设置 max-width,避免固定宽度在小屏幕下溢出。

绝对定位水平居中示意图

绝对定位元素与 float 同时设置时,float 不会继续按照浮动布局生效,通常应直接删除 float 声明。

z-index 与定位的关系

z-index 用于控制元素在同一堆叠上下文中的绘制顺序。它不是简单的“数值越大越靠前”,还会受到堆叠上下文的限制。

常见会创建堆叠上下文的情况包括:

  • 定位元素设置了非 autoz-index
  • position: fixedposition: sticky
  • opacity 小于 1
  • transformfilterperspective 等属性不为默认值;
  • isolation: isolate
  • Flex/Grid 项目设置非 autoz-index

例如:

.layer-a {
  position: relative;
  z-index: 2;
}

.layer-b {
  position: relative;
  z-index: 1;
}

如果 .layer-b 的子元素设置了非常大的 z-index,也不能越过父级 .layer-a 所在的更高层级堆叠上下文。子元素的 z-index 只能在自己的堆叠上下文内部比较。

实际项目建议使用有意义的层级令牌,而不是随意堆叠数字:

:root {
  --z-dropdown: 10;
  --z-sticky: 20;
  --z-modal: 100;
  --z-toast: 1000;
}

不要认为 z-index 大于 2 就一定是冗余的,也不要只依赖 DOM 顺序解决复杂覆盖关系。

relative 属性值

absolute 的包含块作用

position: relative 最常见的用途,是让父元素成为绝对定位子元素的包含块:

<div class="container">
  <div class="item"></div>
</div>
.container {
  position: relative;
  width: 600px;
  height: 400px;
  margin: 0 auto;
  background: #9c27b0;
}

.item {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 300px;
  height: 200px;
  background: #4caf50;
}

relative 建立 absolute 包含块示意图

需要注意,relative 只建立包含块,并不会限制子元素不能超出父元素:

.item {
  position: absolute;
  top: -50px;
  left: -50px;
}

如果希望裁剪超出的部分,通常让同一个元素同时建立定位包含块并负责裁剪:

.container {
  position: relative;
  overflow: hidden;
}

position: relative 本身不负责裁剪,overflow: hidden 才是裁剪规则。但绝对定位后代存在一个特殊情况:如果裁剪元素位于绝对定位元素与其包含块之间,裁剪效果可能不会按预期作用。让裁剪元素同时成为绝对定位元素的包含块,通常可以避免这个问题。

父元素未建立定位包含块时的效果

父元素建立定位包含块并裁剪后的效果

z-index 与父级堆叠上下文

当两个父元素分别创建了堆叠上下文时,子元素的 z-index 只能在各自的父级上下文中比较:

.parent-a {
  position: relative;
  z-index: 2;
}

.parent-b {
  position: relative;
  z-index: 1;
}

.parent-b .child {
  position: absolute;
  z-index: 9999;
}

即使 .childz-index 很大,它也不能越过 .parent-a.parent-b 的父级层级关系。

relative 偏移

相对定位的偏移是相对于元素原来的位置计算的。元素原来的布局空间仍然保留,兄弟元素不会因为它的视觉偏移而重新排列,但元素可能与其他内容重叠:

.container {
  position: relative;
  top: 100px;
  left: 100px;
  width: 600px;
  height: 400px;
  background: #9c27b0;
}

relative 偏移示意图

更合理的使用方式

“relative 最小化影响原则”不是 CSS 规范中的固定规则。实际开发时,应优先让定位关系清晰:

  • 需要让角标相对于卡片定位时,给卡片设置 position: relative
  • 不要为了避免 relative 而使用 margin,因为 margin 会参与普通流,且不会建立绝对定位包含块;
  • 复杂组件可以增加专门的定位上下文,但要明确其尺寸、溢出和层级关系;
  • 如果绝对定位元素应该影响布局,应考虑 Flex、Grid 或普通流,而不是强行使用绝对定位。

例如:

<div class="card">
  <span class="badge">推荐</span>
  <h2>文章标题</h2>
</div>
.card {
  position: relative;
  padding: 1rem;
}

.badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}

relative 作为组件定位上下文示意图

fixed 属性值

position 设置为 fixed 时,元素通常会相对于视口定位,不会随着页面滚动而移动:

<div class="container">固定提示条</div>
body {
  min-height: 2000px;
  margin: 0;
}

.container {
  position: fixed;
  z-index: 20;
  top: 0;
  right: 0;
  left: 0;
  width: min(500px, calc(100% - 2rem));
  box-sizing: border-box;
  margin-inline: auto;
  padding: 1rem;
  background: #2c80c5;
  color: #fff;
  text-align: center;
}

当页面滚动时,元素通常仍保持在视口顶部。设置 toprightbottomleft 可以明确其相对于包含块的偏移;如果没有设置 inset,浏览器会根据固定定位元素的静态位置计算初始位置,实际效果不如显式设置清晰。

fixed 定位元素示意图

fixed 的注意事项

fixed 通常相对于视口,但以下情况可能改变它的定位参考:

  • 祖先设置了 transform
  • 祖先设置了 filterperspective
  • 祖先使用了某些 containwill-change
  • 移动端浏览器的视觉视口发生变化。

另外,固定元素脱离普通流,可能遮挡正文,因此通常需要给正文预留空间:

main {
  padding-top: 4rem;
}

sticky 属性值

sticky 可以理解为普通流布局和滚动约束结合后的定位方式。元素在普通流中保留空间,滚动到指定阈值时会在滚动容器中保持一段距离,但它不会真正把计算值切换成 fixed

基本示例

<div class="container">
  <section>
    <h2 class="title">内容 1</h2>
    <img src="example.jpg" alt="内容 1 配图">
  </section>
  <section>
    <h2 class="title">内容 2</h2>
    <img src="example.jpg" alt="内容 2 配图">
  </section>
  <section>
    <h2 class="title">内容 3</h2>
    <img src="example.jpg" alt="内容 3 配图">
  </section>
</div>
.container {
  width: min(600px, 100%);
  margin: 0 auto;
  background: #eee;
}

section {
  min-height: 600px;
}

.title {
  position: sticky;
  z-index: 1;
  top: 0;
  min-height: 60px;
  margin: 0;
  background: #ff7300;
  color: #fff;
  line-height: 60px;
  text-align: center;
}

section img {
  display: block;
  width: 100%;
  height: auto;
}

sticky 粘性定位示意图

.title 滚动到 top: 0 的阈值时,它会在允许的范围内保持在滚动容器顶部;当其所属的包含块边界限制它继续移动时,它会停止粘滞。它并不是简单地切换为 fixed,也不一定要等到父元素完全离开视口才恢复原来的位置。

sticky 的生效条件

1. 必须设置对应方向的阈值

垂直滚动时通常设置 topbottom,水平滚动时设置 leftright

.header {
  position: sticky;
  top: 0;
}

也可以使用逻辑属性:

.header {
  position: sticky;
  inset-block-start: 0;
}

如果没有设置相关方向的 inset,通常不会产生可见的粘滞效果。

2. 最近的滚动容器会影响 sticky

sticky 会参考最近的滚动容器,而不是固定地参考视口。祖先设置 overflow: autoscrollhidden 时,可能会建立滚动机制或改变粘滞边界。

overflow: hidden 并不总是让 sticky 直接失效,但如果该祖先没有足够的滚动空间,sticky 可能表现得像没有生效。排查时应检查:

  • 哪个祖先是真正发生滚动的容器;
  • 是否有祖先设置了 overflow
  • 滚动容器是否有明确高度;
  • sticky 元素是否有足够的可滚动空间。

3. 父容器必须提供足够空间

粘性元素受到其包含块边界限制。如果父容器高度只等于 sticky 元素本身,几乎没有空间观察到粘滞效果。

4. Flex 和 Grid 布局可能影响效果

如果 sticky 元素在 Flex 或 Grid 容器中被 align-items: stretch 拉伸,可能没有足够的滚动空间。可以根据实际布局尝试:

.sidebar {
  align-self: start;
  position: sticky;
  top: 1rem;
}

5. 同时设置多个 inset 时要结合书写模式判断

topbottomleftright 同时设置时,最终效果与布局方向、元素尺寸和粘滞视图范围有关,不能简单地对所有场景都概括为“某个属性永远优先”。通常建议只设置实际需要的一个方向,例如垂直吸顶只设置 top

6. sticky 元素通常需要背景和层级

如果 sticky 元素没有背景,滚动时后面的内容可能透过它显示;如果需要覆盖正文,还应设置合适的 z-index

.sticky-header {
  position: sticky;
  z-index: 10;
  top: 0;
  background: #fff;
}

五种定位方式对比

是否保留普通流空间常见参考系常见用途
static普通流默认布局
relative自身原始位置建立包含块、轻微偏移
absolute定位包含块角标、弹出层、覆盖元素
fixed通常是视口浮动按钮、固定导航、遮罩
sticky滚动容器和阈值吸顶标题、侧边栏导航

总结

  1. position 决定元素采用普通流、相对、绝对、固定或粘性定位;
  2. relative 不会限制绝对定位子元素的范围,它主要用于建立定位包含块;
  3. absolute 脱离普通流,不会为元素保留原来的布局空间;
  4. fixed 通常相对于视口,但特殊祖先可能改变其包含块;
  5. sticky 不是真正的 relativefixed 动态切换,而是带滚动约束的粘性定位;
  6. topbottom 的百分比通常参考包含块高度,leftright 通常参考包含块宽度;
  7. overflow 决定是否裁剪溢出内容,与 position: relative 建立包含块是两件事;
  8. z-index 需要结合堆叠上下文判断,不能只看数值大小或 DOM 顺序;
  9. 使用绝对定位前,应先确认元素是否真的应该脱离普通流;
  10. 对页面整体布局,优先考虑普通流、Flexbox 和 Grid,把定位用于覆盖、角标、浮层和固定交互元素;
  11. sticky 不生效时,应重点检查 inset 阈值、滚动容器、父级高度、overflow 以及 Flex/Grid 对齐方式。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS