CSS:position控制位置和文档流

定义

positionCSS 中用于控制元素定位方式的核心属性

它的作用是:决定一个 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 不知道它的存在,从顶部开始渲染(可能被盖住)
  • 但因为 .fixedz-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,它会跑到整个页面的右上角!

如果不设置topright,那么它会紧贴着浅蓝父容器的左上角

方向记忆法:在 absolute 里,topright 表示“距离定位父容器对应边的距离”,也就是“在父容器内的位置”。不是偏移量,而是绝对位置描述

写法 含义
top: 20rpx “我的顶部,离父容器顶部 20rpx”
left: 20rpx “我的左边,离父容器左边 20rpx”
bottom: 20rpx “我的底部,离父容器底部 20rpx”
right: 20rpx “我的右边,离父容器右边 20rpx”

案例 5:position: absolute(粘性定位)

它让元素在相对定位固定定位之间切换,是一种非常实用的定位方式。

滚动位置 行为
元素未达到阈值 表现为 relative(相对定位)
元素达到阈值 表现为 fixed(固定定位)

简单来说:元素会在滚动到指定位置时”粘”在那里,直到其父容器滚动出视野。

工作原理示意

正常流位置 → 滚动到阈值 → 变为固定定位 → 离开父容器 → 恢复正常流
    ↓              ↓              ↓              ↓              ↓
 position:    触发 sticky    类似 fixed    到达父容器     跟随文档流
  relative       效果         固定不动       底部          继续滚动

position: sticky 的触发条件

  • **必须设置偏移值:**必须至少设置 topbottomleftright 中的一个值,如果没有设置任何偏移值,sticky 不会生效。设置0值也可以。
  • 父容器必须有足够的高度:
    • sticky 元素只能在其父容器的滚动范围内生效
    • 当滚动到父容器底部时,sticky 元素会停止固定
    • 如果父容器高度不足,可能看不到 sticky 效果
  • **祖先容器不能有 overflow: hidden/auto/scroll:**如果任意祖先元素设置了 overflow: hiddenoverflow: autooverflow: 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>

×

喜欢就点赞,疼爱就打赏