定义
position 是 CSS 中用于控制元素定位方式的核心属性。
它的作用是:决定一个 HTML 元素如何在页面中“摆放”——是按正常顺序排,还是脱离文档流、固定在某处、相对自己偏移等等。
position 告诉浏览器:“这个元素该怎么定位?”
脱离文档流
什么是“文档流”?(正常流)
想象你写作文:
- 字从左到右写
- 行从上到下排
- 每个字都按顺序占位置
在 HTML/CSS 中:
- 块级元素(如
<div>、<p>)独占一行,从上往下堆 - 行内元素(如
<span>、<a>)从左到右排在同一行
这就是 “文档流”(Normal Flow / Document Flow) —— 浏览器默认的排版规则。
<div>A</div>
<div>B</div>
<div>C</div>
效果:每个 div 占一行,彼此知道对方存在。
A
B
C
“脱离文档流”是什么意思?
当一个元素 脱离文档流 后:
它不再占据原本的空间
其他元素会“假装它不存在”,继续按正常流排列
它可以自由叠加、悬浮、覆盖其他内容
**脱离文档流的元素会“悬浮”在页面之上或之下,是否被覆盖,由
z-index决定。
<div class="normal">普通内容</div>
<div class="fixed">固定层</div>
.normal {
height: 200px;
background: lightblue;
}
.fixed {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 50px;
background: red;
z-index: 10; /* 关键! */
}
.fixed脱离文档流 →.normal不知道它的存在,从顶部开始渲染(可能被盖住)- 但因为
.fixed的z-index: 10更高 → 它显示在.normal上方 - 如果你把
z-index设为-1,.fixed就会在.normal下面(可能看不见)
position 的常见取值及效果
| 值 | 是否脱离文档流 | 定位基准 | 常用场景 |
|---|---|---|---|
static |
❌ 否 | — | 默认,无需特殊定位 |
relative |
❌ 否 | 自身原位置 | 微调位置、作为 absolute 的父容器 |
absolute |
✅ 是 | 最近非 static 祖先 |
弹窗、角标、悬浮层 |
fixed |
✅ 是 | 屏幕视口 | 固定按钮、返回顶部(H5) |
sticky |
❌ 否(相对定位) | 视口或滚动容器的边界 | 吸顶导航、分类标题固定 |
默认值是
static—— 所有元素天生就是position: static。
sticky特性的本质是relative+fixed的混合行为:
案例 1:position: static(默认值)
特点:
- 元素按正常文档流排列
top/left等属性无效
<template>
<view class="page">
<view class="demo-title">1. position: static(默认)</view>
<view class="box static-box">我是 static</view>
<!-- 滚动占位内容 -->
<view class="placeholder" v-for="i in 10" :key="i">占位内容 {{ i }}</view>
</view>
</template>
<script>
export default {}
</script>
<style lang="scss">
.page {
padding: 20rpx;
}
.placeholder {
height: 120rpx;
background: #f5f5f5;
margin: 10rpx 0;
padding: 20rpx;
border: 1rpx solid #eee;
}
.demo-title {
font-weight: bold;
margin: 30rpx 0 20rpx;
}
/* 各案例样式写在这里 */
.box {
width: 200rpx;
height: 100rpx;
background: #9C27B0;
color: white;
text-align: center;
line-height: 100rpx;
margin: 20rpx 0;
}
.static-box {
position: static; /* 默认值,可省略 */
top: 100rpx; /* ❌ 无效! */
left: 100rpx; /* ❌ 无效! */
}
</style>
效果:紫色盒子不会偏移,top/left 被忽略。同时,紫色盒子会随着一起滚动。
案例 2:position: fixed(固定定位)
特点:
- 相对于屏幕视口定位
- 滚动时位置不变
<template>
<view class="page">
<view class="demo-title">2. position: fixed(滚动看效果)</view>
<view class="fixed-box">Fixed 按钮</view>
<!-- 滚动占位内容 -->
<view class="placeholder" v-for="i in 10" :key="i">占位内容 {{ i }}</view>
</view>
</template>
<script>
export default {}
</script>
<style lang="scss">
.page {
padding: 20rpx;
}
.placeholder {
height: 120rpx;
background: #f5f5f5;
margin: 10rpx 0;
padding: 20rpx;
border: 1rpx solid #eee;
}
.demo-title {
font-weight: bold;
margin: 30rpx 0 20rpx;
}
/* 各案例样式写在这里 */
.fixed-box {
width: 140rpx;
height: 80rpx;
background: #FF5722;
color: white;
text-align: center;
line-height: 80rpx;
position: fixed;
bottom: 40rpx;
right: 40rpx;
border-radius: 10rpx;
z-index: 999;
}
</style>
效果(在 H5 浏览器中):滚动页面时,红色按钮始终固定在右下角!
案例 3:position: relative(相对定位)
特点:
- 相对于自己原来的位置偏移
- 仍占据原文档空间(后面元素不会上移)
- 文档流位置不变 → 其他元素仍按它“还在原地”来布局
- 视觉位置偏移 → 只是“看起来”挪了,实际占位没变
<template>
<view class="page">
<view class="demo-title">3. position: relative</view>
<view class="box normal">正常盒子</view>
<view class="box relative-box">relative 盒子</view>
<view class="box normal">另一个盒子</view>
</view>
</template>
<script>
export default {}
</script>
<style lang="scss">
.page {
padding: 20rpx;
}
.demo-title {
font-weight: bold;
margin: 30rpx 0 20rpx;
}
/* 各案例样式写在这里 */
.normal {
background: #ccc;
}
.relative-box {
background: #4CAF50;
position: relative;
top: 15rpx; /* 向下偏移 */
left: 50rpx; /* 向右偏移 */
}
</style>
效果:绿色盒子“挪位置”了,但它原来的位置还空着(下面的灰色盒子没上移)。
- 绿色盒子
top: 15rpx; left: 50rpx→ 视觉上向下右移
位置记忆:
| 你想让元素… | 写什么? |
|---|---|
| 向下移动 | top: 正数 |
| 向上移动 | bottom: 正数 或 top: 负数 |
| 向右移动 | left: 正数 |
| 向左移动/ 具体位置 | right: 正数 或 left: 负数 |
案例 4:position: absolute(绝对定位)
特点:
- 脱离文档流(不占空间)
- 相对于最近的非 static 祖先定位
- 如果父容器是
static(默认) → 继续往上找 - 如果祖父容器是
relative/absolute/fixed→ 以它为参考 - 如果一直找到
<html>都没找到 → 以初始包含块(通常是整个屏幕)为参考
- 如果父容器是
<template>
<view class="page">
<view class="demo-title">4. position: absolute</view>
<view class="parent-box">
<view class="absolute-box">absolute</view>
</view>
</view>
</template>
<script>
export default {}
</script>
<style lang="scss">
.page {
padding: 20rpx;
}
.demo-title {
font-weight: bold;
margin: 30rpx 0 20rpx;
}
/* 各案例样式写在这里 */
.parent-box {
width: 100%;
height: 200rpx;
background: #e3f2fd;
position: relative; /* 👈 关键!创建定位上下文 */
margin: 20rpx 0;
}
.absolute-box {
width: 120rpx;
height: 80rpx;
background: #2196F3;
color: white;
text-align: center;
line-height: 80rpx;
position: absolute;
top: 20rpx; /* 距离父容器顶部 20rpx */
right: 20rpx; /* 距离父容器右边 20rpx */
}
</style>
效果:蓝色盒子贴在浅蓝父容器的右上角。
如果去掉父容器的 position: relative,它会跑到整个页面的右上角!
如果不设置top和right,那么它会紧贴着浅蓝父容器的左上角
方向记忆法:在 absolute 里,top 和 right 表示“距离定位父容器对应边的距离”,也就是“在父容器内的位置”。不是偏移量,而是绝对位置描述!
| 写法 | 含义 |
|---|---|
top: 20rpx |
“我的顶部,离父容器顶部 20rpx” |
left: 20rpx |
“我的左边,离父容器左边 20rpx” |
bottom: 20rpx |
“我的底部,离父容器底部 20rpx” |
right: 20rpx |
“我的右边,离父容器右边 20rpx” |
案例 5:position: absolute(粘性定位)
它让元素在相对定位和固定定位之间切换,是一种非常实用的定位方式。
| 滚动位置 | 行为 |
|---|---|
| 元素未达到阈值 | 表现为 relative(相对定位) |
| 元素达到阈值 | 表现为 fixed(固定定位) |
简单来说:元素会在滚动到指定位置时”粘”在那里,直到其父容器滚动出视野。
工作原理示意
正常流位置 → 滚动到阈值 → 变为固定定位 → 离开父容器 → 恢复正常流
↓ ↓ ↓ ↓ ↓
position: 触发 sticky 类似 fixed 到达父容器 跟随文档流
relative 效果 固定不动 底部 继续滚动
position: sticky 的触发条件
- **必须设置偏移值:**必须至少设置
top、bottom、left或right中的一个值,如果没有设置任何偏移值,sticky 不会生效。设置0值也可以。 - 父容器必须有足够的高度:
- sticky 元素只能在其父容器的滚动范围内生效
- 当滚动到父容器底部时,sticky 元素会停止固定
- 如果父容器高度不足,可能看不到 sticky 效果
- **祖先容器不能有
overflow: hidden/auto/scroll:**如果任意祖先元素设置了overflow: hidden、overflow: auto或overflow: scroll,sticky 定位可能会失效(因为滚动容器被改变了) - **必须在滚动容器内:**页面需要有滚动空间才能触发 sticky,如果内容没有超出视口,sticky 不会有任何效果。
<template>
<view class="container">
<!-- 使用 scroll-view 确保可滚动(小程序推荐) -->
<scroll-view scroll-y class="scroll-container" >
<!-- 吸顶分类标题 -->
<view
v-for="(group, index) in dataList"
:key="index"
class="group"
>
<!-- sticky 标题 -->
<view class="sticky-title">{{ group.title }}</view>
<!-- 列表内容 -->
<view
v-for="(item, idx) in group.items"
:key="idx"
class="list-item"
>
{{ item }}
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
dataList: [
{
title: 'A',
items: ['刘备', '阿斗', '赵云', '马超']
},
{
title: 'B',
items: ['白居易', '包拯', '扁鹊']
},
{
title: 'C',
items: ['曹操', '曹丕', '曹植', '蔡文姬']
},
{
title: 'D',
items: ['杜甫', '狄仁杰', '董永']
},
{
title: 'E',
items: ['苏轼', '孙权', '司马懿']
}
]
}
}
}
</script>
<style scoped>
.container {
height: 100vh; /* 🔹 这里必须是具体的值,否则就回失效 */
background-color: #f5f5f5;
}
.scroll-container {
height: 100%; /* 🔹 关键:scroll-view 必须占满容器 */
}
.group {
/* 父容器不能设置 overflow,否则 sticky 失效 */
}
/* 🔹 核心:sticky 定位 */
.sticky-title {
position: sticky;
top: 0px; /* 必须指定!距离顶部 0px 时吸顶 */
/* 样式 */
height: 44px;
line-height: 44px;
padding-left: 16px;
background-color: #007aff;
color: #ffffff;
font-size: 16px;
font-weight: bold;
/* 确保层级 */
z-index: 100;
}
.list-item {
height: 60px;
line-height: 50px;
padding-left: 16px;
background-color: #ffffff;
border-bottom: 1px solid #e0e0e0;
font-size: 14px;
color: #333333;
}
</style>