新手小白学前端day4: Position定位

Day4 学习文档:Position 定位实战

1. 今天要掌握什么

Day4 的目标是把"元素放哪儿"这件事彻底搞明白:

  • 理解 relativeabsolutefixedsticky 的区别
  • 知道每种定位在什么场景下最合适
  • 能做出 3 个常见交互:吸顶导航、角标、回到顶部按钮

2. 大白话理解 Position

可以把页面想象成一张地图,普通元素按"排队规则"从上到下放置。
position 就是告诉浏览器:这个元素是否要"偏离原队列"。

  • static:默认值,老老实实排队
  • relative:还在队列里,但允许"微调位置"
  • absolute:脱离队列,贴着某个参考盒子定位
  • fixed:脱离队列,直接贴着屏幕定位
  • sticky:平时排队,滚动到阈值后吸附

2.1 两个必须懂的基础词:viewport 和文档流


一、文档流(Normal Flow)

大白话:就是网页里的元素"排队"的方式,默认情况下,它们按照你在HTML里写的顺序,一个接一个地自动摆放。

  • 块级元素 (比如 <div><p><h1>):就像地铁里的车厢,每个独占一整节,竖着排,上一个在顶上,下一个在底下,不会并排。
  • 行内元素 (比如 <span><a><strong>):就像排队买票的人,大家并排站在一起,从左到右,一行不够了就自动换到下一行。

一句话:文档流就是"正常排队",你什么都不改,它就这么排。


二、viewport(视口)

大白话 :就是你手机或电脑屏幕上用来显示网页的那个矩形区域

  • 在电脑上,视口就是浏览器窗口的内部(不包括工具栏、地址栏)。
  • 在手机上,视口比较特殊:因为手机屏幕窄,早期网页都是为电脑设计的(宽度至少960px),如果手机用真实屏幕宽度(比如375px)去显示,网页会被挤得乱七八糟。所以手机浏览器默认用一个虚拟的宽视口(通常是980px)来加载网页,然后缩放显示。这就导致你看不清字,需要手动放大。

**<meta name="viewport"> 标签的作用**:告诉手机浏览器:"别用那个虚拟宽视口了,就用我的实际屏幕宽度来布局"。常见写法:

html 复制代码
<meta name="viewport" content="width=device-width, initial-scale=1.0">

意思是:

  • width=device-width:把视口宽度设成手机屏幕的实际宽度(比如375px)。
  • initial-scale=1.0:不缩放,1个CSS像素对应1个屏幕物理像素。

一句话:viewport 就是"你看网页的那个框框",移动端必须设置那个meta标签,否则网页会像缩小的蚂蚁。


三、二者关系

  • 文档流 是元素在视口内的排列方式。
  • 视口 是容纳文档流的容器大小。

举个例子:你把一排积木(文档流)放在一个盒子里(视口)。盒子宽了,积木可能并排;盒子窄了,积木可能换行。

移动端如果不设置 viewport,盒子(视口)会默认宽980px,导致你的积木(元素)看起来很迷你,用户必须双指缩放才能看清。设置了之后,盒子宽度就是手机屏幕宽度,你按正常尺寸写CSS就行。

希望这样解释你彻底明白了。如果还有模糊的地方,可以继续问。


3. 四种核心定位方式(更直观版)

先记一句总口诀:

  • relative站在原地,可微调
  • absolute脱离队伍,找爹定位
  • fixed钉在屏幕,不跟页面走
  • sticky先正常滚,后吸顶

3.1 position: relative(站在原地,可微调)

生活类比:

你在排队时,脚还在原位置,但身体可以往前后左右挪一点。

你会看到的效果:

  • 这个元素原本位置还占着,不会让后面的元素补上来
  • 可以用 top/right/bottom/left 微调显示位置

最常见用途(不是"挪自己",而是"给孩子当参照物"):

  • 给卡片加 position: relative,让内部角标用 absolute 对齐这张卡片

一句话判断:
需要一个"定位锚点"时,先上 relative

3.2 position: absolute(脱离队伍,找爹定位)

生活类比:

一个人离开队伍,跑去贴着"最近的定位锚点"站位。

你会看到的效果:

  • 元素脱离文档流,原位置会被其他元素"顶上来"
  • 它会找最近的、position 不是 static 的祖先作为参照
  • 如果没找到,就相对页面定位(常见"跑飞")

典型场景:

  • 卡片角标、关闭按钮、图片文案浮层

一句话判断:
想让元素"贴着某个盒子角落"时,用 absolute + 父级 relative

3.3 position: relativeabsolute 的关系(大白话)

3.3.1 relative(相对定位)

大白话 :元素原本在文档流里占着位置 (排队占位),你可以通过 top/left/right/bottom 让它相对于自己的原位置挪动一下。挪动后,原来的坑位依然空着(其他元素不会挤过来)。

  • 常用场景:给绝对定位的父元素做"参照物"。
3.3.2 absolute(绝对定位)

大白话 :元素完全脱离文档流 (不再排队,也不再占位),其他元素会忽略它,直接填补它原来的位置。它的位置参考系是最近的那个设置了 position(非 static)的祖先元素 。如果找不到这样的祖先,就参考视口(但严格说是初始包含块,可以理解为视口)。

3.3.3 它们最经典的配合:父 relative,子 absolute

这是为了让子元素相对于父元素进行绝对定位。

例子

html 复制代码
<div class="parent" style="position: relative;">
  <div class="child" style="position: absolute; top: 0; right: 0;">角标</div>
</div>
  • 父元素 relative:不挪动自己,只是建立一个定位参照系
  • 子元素 absolute:相对于父元素左上角定位,top:0; right:0 就贴在父元素右上角。

为什么父元素不用 absolute 因为父元素如果 absolute 也会脱离文档流,破坏布局。用 relative 最安全(它不脱离文档流)。

3.3.4 对比表格
特性 relative absolute
是否脱离文档流 ❌ 不脱离,原位置保留 ✅ 脱离,原位置被其他元素占据
定位参照物 自身原本的位置 最近的非 static 祖先(如果没有,则视口)
能否用 top/left 移动 能,相对于自身原位置 能,相对于参照物
常见用途 作为绝对定位的容器、微调位置 浮层、角标、弹出菜单
3.3.5 一个帮你记忆的生活类比
  • **relative**:就像你站在排队的位置上,可以稍微往前探一点身子,但你的脚还在原地(别人不能占你的位)。
  • **absolute**:你从队伍里走出来,站在某个参照物(比如墙壁)旁边。你的原位置立刻被后面的人占了。
  • relative + 子 absolute:你对墙壁(父元素)说:"我要站在你右上角"。墙壁说:"好,我原地不动,你相对于我站。"
3.3.6 一个特殊但重要的点

如果某个祖先元素设置了 transformperspectivefilter 等属性,它会成为 absolute 的参照物(类似 relative),这有时会导致预期外的定位。


总结一句话

  • relative 是"相对自己原位置微调,不脱队"。
  • absolute 是"脱队去找最近的带定位的祖先,没有就找视口"。
  • 组合使用时,父 relative 给子 absolute 当参照物。

3.4 position: fixed(钉在屏幕,不跟页面走)

生活类比:

把便签纸贴在你的手机屏幕上,不管页面怎么滑,便签都在同一个位置。

你会看到的效果:

  • 元素固定在视口(viewport)上
  • 页面滚动时它不动
  • 也脱离文档流,可能遮挡内容

典型场景:

  • 回到顶部按钮
  • 悬浮客服入口

一句话判断:
想"永远在屏幕可见区域"就用 fixed

3.5 position: sticky(先正常滚,后吸顶)

生活类比:

便利贴一开始贴在文档某一行,滚动到顶部后它被"吸"住,不再继续上去。

你会看到的效果:

  • 在阈值前,它和普通元素一样参与文档流
  • 达到阈值(如 top: 0)后,像 fixed 一样吸附
  • 常需要配 z-index 和背景色,避免被内容盖住

典型场景:

  • 吸顶导航
  • 左侧目录跟随

一句话判断:
想要"滚动到某点才固定",选 sticky


4. 你当前页面里的实战映射

4.1 吸顶导航(sticky)

css 复制代码
.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
}

解释:导航滚动到页面顶部后会"钉住"。

4.2 角标(relative + absolute)

css 复制代码
.badge-card {
  position: relative;
}

.badge {
  position: absolute;
  top: -10px;
  right: -10px;
}

解释:角标相对卡片定位,而不是相对整个页面乱跑。

4.3 回到顶部按钮(fixed)

css 复制代码
.back-top {
  position: fixed;
  right: 16px;
  bottom: 16px;
}

解释:无论页面滚动到哪里,按钮都固定在屏幕右下角。


5. 常见坑(重点)

  1. absolute 位置跑偏
  • 原因:父元素没设 position: relative
  1. sticky 不生效
  • 常见原因:没写 top;或父容器有 overflow 限制
  1. fixed 挡住内容
  • 解决:给主内容留底部空间,或调整按钮位置
  1. 层级覆盖异常
  • 解决:配合 z-index 控制层级(前提是元素有定位)

6. 提交前自测清单

  • 顶部导航滚动时可吸附
  • 角标稳定在卡片右上角
  • 回到顶部按钮始终固定在右下角
  • 手机宽度下无明显遮挡或溢出
  • 你能说清这四种定位的适用场景

7. 今天的学习产出模板(可复制到 README)

md 复制代码
## Day4 学习总结(Position)

### 我做了什么
- 实现 sticky 吸顶导航
- 实现 relative + absolute 角标
- 实现 fixed 回到顶部按钮

### 我学会了什么
- 四种常见定位方式的差异
- absolute 的参照物查找规则
- sticky 的触发条件

### 我遇到的问题
- (填写你今天遇到的问题)

### 我如何解决
- (填写解决过程)

8. CSS 选择器速记(结合 .fixed-demo

你在 Day4 页面里看到这段:

css 复制代码
.fixed-demo {
  position: fixed;
  top: 12px;
  right: 12px;
}

.fixed-demo 里的 . 是什么?

. 表示 class 选择器,意思是:

  • 选中所有 class="fixed-demo" 的元素
  • 给这些元素应用样式

对应 HTML:

html 复制代码
<div class="fixed-demo">我是 fixed 对照条</div>

常见选择器符号(新手高频)

  • .demo:class 选择器(最常用)
  • #demo:id 选择器(页面唯一)
  • div:标签选择器
  • A B:后代选择器(A 内任意层级的 B)
  • A > B:子代选择器(A 的直接子元素 B)

一句话记忆

  • . 找 class
  • # 找 id
  • 空格找后代
  • > 找亲儿子(直接子元素)

9. HTML 多个 class:class="card badge-card"

它是什么意思?

class="card badge-card" 表示这个元素同时拥有两个 class

  • card
  • badge-card

在 CSS 里,只要选择器匹配,就会同时应用:

css 复制代码
.card { ... }
.badge-card { ... }

为什么要这样写?

常见原因是"组合样式":

  • card:通用卡片外观(白底、边框、圆角、内边距)
  • badge-card:额外加 position: relative,给角标当定位参照物

这样拆分后,别的卡片可以只复用 card,不必复制一堆样式。

如果两个规则冲突了怎么办?

如果 .card.badge-card 都写了同一个属性(比如 padding),最终生效取决于:

  1. **选择器优先级(specificity)**更高者优先
  2. 优先级相同,通常 后写的规则覆盖先写的规则(同文件内从上到下)

CSS 里还有一种"链式 class"(可选进阶)

css 复制代码
.card.badge-card {
  /* 必须同时有 card 和 badge-card 才会命中 */
}

这和 HTML 写多个 class 是配套的,用来表达"只在特定组合下生效"的样式。

更系统的解释请看下一节:## 10. CSS 优先级 + 链式选择器(进阶但很有用)


10. CSS 优先级 + 链式选择器(进阶但很有用)

10.1 链式选择器是什么?

链式选择器(也叫"复合选择器"的一种常见写法):

css 复制代码
.card.badge-card { }

含义:元素必须同时满足

  • class="card"
  • 也有 class="badge-card"

所以它比单独的 .card 更"挑剔",命中范围更小。

对比:

html 复制代码
<section class="card">A</section>
<section class="card badge-card">B</section>
  • .card:A 和 B 都会命中
  • .card.badge-card:只有 B 命中

10.2 链式选择器会不会让优先级变高?

会。链式 class 相当于把多个 class 条件叠在一起,通常比单个 class 更具体

直觉记忆:

  • .card:1 个 class
  • .card.badge-card:2 个 class(更具体)

所以在冲突时,.card.badge-card 往往更容易赢过 .card

10.3 CSS 优先级(specificity)到底比什么?

当两条规则都设置了同一个属性(比如 padding),浏览器要决定用哪条,会先看 优先级 ,再看 书写顺序(同优先级时,后写覆盖先写)。

新手最常用的优先级直觉(从低到高):

  1. 标签选择器 (如 divsection
  2. class 选择器 (如 .card
  3. id 选择器 (如 #intro
  4. 行内样式style="...",一般不推荐大面积使用)
  5. **!important**(尽量别当常规武器)

说明:真实计算比这个更细(还会统计选择器里 class/id/标签的数量),但上面的顺序足够你日常排错。

10.4 两个很容易踩坑的点

  1. 以为"写在后面就一定赢" 不一定。如果对方选择器优先级更高,你写在后面也可能无效。
  2. 链式选择器写错 .card .badge-card(中间有空格)是后代选择器,不是链式选择器。
    .card.badge-card(中间没空格)才是"同时有两个 class"。

10.5 结合你 Day4 页面的一个判断练习

假设同时存在:

css 复制代码
.card { padding: 16px; }
.badge-card { padding: 24px; }
.card.badge-card { padding: 20px; }

对一个 class="card badge-card" 的元素:

  • 三个规则都匹配
  • 最终 padding 通常会落在 .card.badge-card(更具体)

如果你发现结果不符合预期,打开 DevTools 看 Computed 面板,能看到最终生效规则与被覆盖原因。


11. border-radius: 999px 是什么"magic"?

它不是魔法,而是一个常见技巧:
把圆角半径写得非常大,让浏览器自动夹到可用最大值。

11.1 为什么写 999px 也不会"溢出"?

浏览器会做限制:圆角不可能超过元素几何允许的范围。

所以你写 999px,实际会被"裁到最大可行圆角"。

效果通常是:

  • 长条按钮 -> 胶囊形(两端很圆)
  • 接近正方形的按钮 -> 接近圆形

11.2 和 border-radius: 50% 有什么区别?

  • 999px:给"很大固定值",常用于按钮、标签,尺寸变化时也容易保持圆润
  • 50%:按元素自身尺寸比例计算,常用于正方形头像变圆

一句话理解:
999px 更像"我要尽可能圆",50% 更像"按比例圆"。

11.3 在你 Day4 页面里的实际用途

你的"回到顶部"按钮如果用了:

css 复制代码
.back-top {
  border-radius: 999px;
}

它会变成胶囊风格,更像悬浮操作按钮。

11.4 常见使用场景

  • 悬浮按钮(回到顶部、客服入口)
  • 小标签(Tag / Badge)
  • 导航中的胶囊按钮

11.5 小提醒

border-radius 只影响"圆角外观",不影响元素布局流。

你仍需要配合 paddingline-heightwidth/height 去控制按钮最终形态。


附录:完整 index.html 代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Day4 Position 练习</title>
    <style>
      *, *::before, *::after {
        box-sizing: border-box
      }

      .fixed-demo {
        position: fixed;
        top: 0;
        right: 20px;
        z-index: 100;
        background: yellow;
        padding: 12px 16px;
        border-bottom: 1px solid #e5e5e5;
      }

      .pre-scroll {
        height: 280px;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #475569;
        background: linear-gradient(180deg, #dbeafe 0%, #eff6ff 100%);
        border-bottom: 1px solid #bfdbfe;
      }

      .topbar {
        position: sticky;
        top: 0;
        z-index: 10;
        background: #ffffff;
        border-bottom: 1px solid #e5e7eb;
      }

      .topbar-inner {
        max-width: 960px;
        margin: 0 auto;
        padding: 12px 16px;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }

      .topbar a {
        text-decoration: none;
        color: #1d4ed8;
        margin-left: 10px;
      }

      .back-top {
        position:fixed;
        right: 16px;
        bottom: 16px;
        border: 1px solid #1d4ed8;
        border-radius: 999px;
        background: #2563eb;
        color: #fff;
        text-decoration: none;
        padding: 10px 14px;
      }

      .back-top:hover {
        background: #1d4ed8;
      }

      .filler {
        min-height: 1200px;
      }

      .container {
        /* 设置了最大宽度,并且居中显示,并且有内边距 */
        max-width: 960px;
        margin: 0 auto;
        padding: 24px 16px 80px;
      }

      /* 卡片样式 */
      .card {
        background: #fff;
        border: 1px solid #e5e7eb;
        border-radius: 10px;
        padding: 16px;
        margin-bottom: 16px;
      }

      /* 角标卡片样式 */
      .badge-card {
        position: relative;
      }

      /* 角标样式 */
      .badge {
        /* 绝对定位,相对于父元素 */
        position: absolute;
        /* 距离父元素上边10px */
        top: -10px;
        /* 距离父元素右边10px */
        right: -10px;
        /* 背景颜色 */
        background: #ef4444;
        /* 文字颜色 */
        color: #fff;
        /* 字体大小 */
        font-size: 12px;
        /* 内边距 */
        padding: 4px 8px;
        /* 圆角 */
        border-radius: 999px;
      }
    </style>
  </head>
  <body>
    <div class="fixed-demo">我是fixed(一直钉在屏幕右上角)</div>

    <section class="pre-scroll">
      <p>先向下滚动,再观察 topbar 何时开始吸顶</p>
    </section>

    <header class="topbar">
      <div class="topbar-inner">
        <strong>Day4 Position 实战</strong>
        <nav>
          <a href="#intro">介绍</a>
          <a href="#absolute-relative">角标</a>
          <a href="#sticky-fixed">笔记</a>
        </nav>
      </div>
    </header>

    <main class="container">
      <section class="card" id="intro">
        <h1>Day4 Position 练习页面</h1>
        <p>本日目标:掌握 relative / absolute / fixed / sticky 的使用场景。</p>
      </section>
      <section  class="card badge-card" id="absolute-relative">
        <span class="badge">NEW</span>
        <h2>absolute + relative 角标示例</h2>
        <p>父元素用 <code>position: relative</code>,角标用 <code>position: absolute</code>。</p>
      </section>
      <section class="card" id="sticky-fixed">
        <h2>sticky + fixed 说明</h2>
        <p>顶部导航使用 <code>position: sticky</code>,滚动到顶部后保持可见。</p>
        <p>右下角按钮使用 <code>position: fixed</code>,始终固定在视口位置。</p
      </section>
      <section class="filler"></section>

      <a href="#intro" class="back-top">回到顶部</a>
    </main>
    
  </body>

</html>
相关推荐
fishmemory7sec2 小时前
Vue大屏自适应容器组件:v-scale-screen
前端·javascript·vue.js
饺子不吃醋2 小时前
Promise原理、手写与 async、await
前端·javascript
PILIPALAPENG2 小时前
第3周 Day 2:Function Calling —— 让 Agent 听懂人话,自己干活
前端·人工智能·python
袋鼠云数栈UED团队2 小时前
基于 OpenSpec 实现规范驱动开发
前端·人工智能
JarvanMo2 小时前
GetX 作者的 GitHub 账号被封,又默默恢复了——但问题远没有解决
前端
大黄说说3 小时前
HTML5语义化标签:从div到article与section的进化之路
前端·html·html5
帅小伙―苏3 小时前
力扣42接雨水
前端·算法·leetcode
糯米团子7493 小时前
react速通-2
前端·react.js·前端框架
心连欣3 小时前
从静态页面到动态交互:DOM操作的核心API解析
前端·javascript·api