CSS 定位

position 属性控制元素在页面上的定位方式。CSS 提供了五种定位模式,各有不同的参照系和行为。

五种定位模式速览

参照系脱离文档流典型用途
static—(默认流)常规布局(默认值)
relative元素自身原本位置微调位置,为绝对定位设置参照
absolute最近的非 static 祖先弹窗、下拉菜单、覆盖层
fixed视口(浏览器窗口)固定导航栏、浮动按钮、模态框
sticky滚动容器否/是吸顶导航、表格固定表头

position: static — 默认值

所有元素默认为 static。它遵循正常的文档流,top/right/bottom/leftz-index 对它无效。

css
div {
  position: static; /* 默认值,通常不需要显式写 */
}

position: relative — 相对定位

相对于元素原本的位置进行偏移,不脱离文档流(原位置保留空白):

css
.adjust {
  position: relative;
  top: -4px;    /* 向上偏移 4px */
  left: 10px;   /* 向右偏移 10px */
}

relative 最常用的场景不是偏移自身,而是作为子元素 absolute 定位的参照容器

css
.parent {
  position: relative; /* 为子元素创建定位上下文 */
}
.child {
  position: absolute;
  top: 0;
  right: 0; /* 位于父元素右上角 */
}
html
<div class="parent" style="width:300px;height:100px;background:#f1f5f9;border:2px solid #e2e8f0">
  <div class="child" style="background:#2563eb;color:white;padding:4px 8px">右上角</div>
</div>

一个常见的 bug:absolute 定位的子元素”飞到了页面左上角”。这是因为它的父元素、祖先元素都没有 position: relative(或 absolute/fixed),导致子元素相对于 <body> 定位。给父元素加 position: relative(不改动布局)即可解决。

position: absolute — 绝对定位

相对于最近的非 static 祖先元素定位,脱离文档流(不占空间):

css
/* 右上角的角标 */
.card {
  position: relative; /* 创建定位上下文 */
}
.badge {
  position: absolute;
  top: -8px;
  right: -8px;
  background: #ef4444;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 0.75rem;
}

/* 居中对齐(绝对定位 + transform) */
.modal {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%); /* 向左、向上各移动自身宽高的一半,实现精确居中 */
}

如果没有定位祖先,absolute 元素会相对于 <html><body> 定位。

position: fixed — 固定定位

相对于浏览器视口定位,脱离文档流。滚动页面时,元素固定在屏幕上的不变位置:

css
/* 固定顶部导航栏 */
.topbar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  background: white;
  z-index: 100;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

/* 右下角浮动按钮 */
.fab {
  position: fixed;
  bottom: 24px;
  right: 24px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: #2563eb;
  color: white;
}

position: fixed 的参照系通常是视口,但如果父元素有 transformperspectivefilter 属性,fixed 会退化(相对于该父元素定位而非视口)。这是 CSS 的一个反直觉行为。

position: sticky — 粘性定位

stickyrelativefixed 的混合体——在滚动到临界点前像 relative,到达临界点后像 fixed

css
/* 吸顶导航 */
.section-header {
  position: sticky;
  top: 0; /* 当元素顶部到达视口顶部时开始固定 */
  background: white;
  z-index: 10;
}

/* 表格固定表头 */
thead th {
  position: sticky;
  top: 0;
  background: #f8fafc;
}

sticky 的几个前提:必须设置 top/right/bottom/left 中的至少一个(否则与 relative 无异);父元素不能设 overflow: hidden(否则 sticky 失效,因为滚动容器变成父元素);父元素高度必须大于 sticky 元素(否则不需滚动就可见全部内容)。

z-index — 叠放顺序

当多个定位元素重叠时,z-index 决定谁在上方:

css
.layer1 { z-index: 1; }    /* 下层 */
.layer2 { z-index: 2; }    /* 中层 */
.layer3 { z-index: 10; }   /* 最上层 */
html
<div style="position:relative;height:120px">
  <div class="layer1" style="position:absolute;top:0;left:0;width:120px;height:120px;background:#93c5fd">z-index: 1</div>
  <div class="layer2" style="position:absolute;top:20px;left:20px;width:120px;height:120px;background:#60a5fa">z-index: 2</div>
  <div class="layer3" style="position:absolute;top:40px;left:40px;width:120px;height:120px;background:#2563eb;color:white">z-index: 10</div>
</div>

z-index 只对定位元素(position 不是 static)和 flex/grid 子元素生效。普通的 static 元素设置 z-index 无效。此外 z-index层叠上下文影响——拥有对比属性的定位元素会创建独立的 z-index 作用域。

常用场景汇总

需求方案
卡片右上角的”新品”角标relative + 子 absolute
固定在顶部的导航栏fixed + top: 0
固定在右下角的按钮fixed + bottom + right
滚动列表的分组标题吸顶sticky + top
模态弹窗居中fixed + top: 0; right: 0; bottom: 0; left: 0(或简写 inset: 0) + flex/grid 居中
拉到最底部时出现的加载更多sticky + bottom: 0