别再写JS监听滚动了!一行CSS搞定导航固定+通讯录效果(附3个案例)

各位前端小伙伴,今天我们来聊聊------position: sticky。无论你是刚入行的新人,还是有一定经验的开发者,掌握 position: sticky都能让你在开发页面时游刃有余。 position: sticky 是一个非常实用的 CSS 属性,它可以让元素在滚动时"粘"在某个位置,像是 相对定位固定定位 的混合体。简单来说,在元素跨越特定阈值之前,它表现为相对定位;一旦跨越阈值,就变成固定定位,直到其容器滚出视口。

通俗点来说,sticky 就是: "在父容器范围内,滚动到指定位置就固定,父容器滚走它就跟着滚走"

它不像 fixed 那样死死钉在屏幕上一个地方不动,也不像 relative 那样完全随波逐流。它是两者的"混血儿",聪明又灵活。

所以做网页时,你想让某个元素(导航栏、标题、侧边栏)在滚动时"粘"在某个位置,但又不想让它超出自己的地盘,用 sticky 就对了!

下面详细介绍它的用法,并附上可直接运行的代码实例。


1. 基本语法

css

css 复制代码
.sticky-element {
  position: sticky;
  top: 0;        /* 可选:left/bottom/right,至少指定一个阈值 */
}

必须指定 topbottomleftright 中的一个,元素才会在到达该阈值时"粘"住。


2. 工作原理

  • 元素在父容器范围内时,遵循正常文档流(相对定位)。
  • 当滚动到指定阈值(例如 top: 10px)时,元素变为固定定位(相对于视口),直到其父容器完全滚出视口。
  • 关键限制 :粘性效果只在父容器内生效,且父容器不能设置 overflow: hiddenoverflow: auto 等(会破坏粘性)。

3. 📁案例一:粘性导航栏

文件名建议sticky-nav-demo.html
效果:导航栏在滚动到顶部时固定,始终可见。

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>案例1:粘性导航栏 · position: sticky</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: 'Segoe UI', Roboto, sans-serif;
      background: linear-gradient(145deg, #f1f5f9 0%, #e6edf5 100%);
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
    }
    .card {
      max-width: 700px;
      width: 100%;
      background: white;
      border-radius: 40px;
      box-shadow: 0 30px 50px -20px rgba(0,30,60,0.25);
      padding: 30px;
    }
    h2 {
      font-weight: 550;
      color: #0f172a;
      margin-bottom: 6px;
      font-size: 2rem;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    h2 span {
      background: #2563eb;
      color: white;
      font-size: 0.9rem;
      padding: 4px 12px;
      border-radius: 30px;
    }
    .desc {
      color: #475569;
      margin-bottom: 30px;
      padding-left: 12px;
      border-left: 5px solid #2563eb;
      background: #f8fafc;
      padding: 12px 18px;
      border-radius: 20px;
    }
    /* 演示区域:固定高度+滚动 */
    .demo-scroll-box {
      height: 380px;
      overflow-y: auto;
      border-radius: 24px;
      background: #ffffff;
      border: 2px solid #d9e2ef;
      scroll-behavior: smooth;
    }
    .demo-header {
      background: #cbd5e1;
      padding: 20px;
      text-align: center;
      font-weight: 600;
      color: #1e293b;
    }
    .sticky-nav {
      position: sticky;
      top: 0;
      background: #0f172a;
      color: white;
      padding: 16px 20px;
      text-align: center;
      font-weight: 500;
      letter-spacing: 0.8px;
      box-shadow: 0 6px 12px rgba(0,0,0,0.1);
      z-index: 10;
    }
    .content p {
      background: #f9f9fc;
      padding: 18px 22px;
      margin: 20px;
      border-radius: 20px;
      border: 1px solid #e2e8f0;
      box-shadow: 0 2px 5px rgba(0,0,0,0.02);
    }
    .note {
      margin-top: 30px;
      background: #fef9c3;
      border-left: 6px solid #eab308;
      padding: 16px 22px;
      border-radius: 24px;
      color: #854d0e;
    }
  </style>
</head>
<body>
<div class="card">
  <h2>📌 案例1 <span>sticky</span></h2>
  <div class="desc">⚓ 粘性导航栏 --- 滚动时自动吸附在顶部,无需JavaScript。</div>

  <!-- 可滚动的演示区域 -->
  <div class="demo-scroll-box">
    <div class="demo-header">我是普通头部(会滚走)</div>
    <div class="sticky-nav">🧲 导航栏 · 粘在顶部 (top:0)</div>
    <div class="content">
      <p>📄 第1条内容:继续向下滚动,观察导航栏。</p>
      <p>📄 第2条内容:它会一直固定在顶部。</p>
      <p>📄 第3条内容:直到父容器滚出视口才消失。</p>
      <p>📄 第4条内容:原理是 position: sticky; top:0。</p>
      <p>📄 第5条内容:兼容现代浏览器,简单高效。</p>
      <p>📄 第6条内容:可以替代传统的JS监听滚动。</p>
      <p>📄 第7条内容:再加一条,看看滚动条长度。</p>
    </div>
  </div>

  <div class="note">
    💡 核心点:<code>position: sticky;</code> 必须搭配 <code>top</code> / <code>bottom</code> 使用;父容器不能设置 <code>overflow:hidden</code>。
  </div>
</div>
</body>
</html>

效果 :当滚动使导航栏触碰到视口顶部时,它会固定在上方,直到父容器 .content 完全离开视口。


📁 案例二:粘性章节标题(通讯录效果)

文件名建议sticky-sections-demo.html
效果:每个分组的标题在滚动到顶部时固定,直到被下一个标题推上去。

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>案例2:粘性章节标题 · position: sticky</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: 'Segoe UI', Roboto, sans-serif;
      background: #eef2f6;
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
    }
    .card {
      max-width: 600px;
      width: 100%;
      background: white;
      border-radius: 40px;
      box-shadow: 0 30px 50px -20px rgba(0,20,40,0.25);
      padding: 30px;
    }
    h2 {
      font-weight: 550;
      color: #0f172a;
      margin-bottom: 6px;
      font-size: 2rem;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    h2 span {
      background: #2563eb;
      color: white;
      font-size: 0.9rem;
      padding: 4px 12px;
      border-radius: 30px;
    }
    .desc {
      color: #475569;
      margin-bottom: 30px;
      padding-left: 12px;
      border-left: 5px solid #2563eb;
      background: #f8fafc;
      padding: 12px 18px;
      border-radius: 20px;
    }
    /* 可滚动容器 */
    .scroll-sections {
      max-height: 420px;
      overflow-y: auto;
      border-radius: 24px;
      background: #ffffff;
      border: 2px solid #d1dbe9;
    }
    .section {
      background: white;
    }
    .sticky-title {
      position: sticky;
      top: 0;
      background: #2563eb;
      color: white;
      padding: 12px 20px;
      margin: 0;
      font-size: 1.4rem;
      font-weight: 600;
      border-bottom: 3px solid #1e3f8f;
      box-shadow: 0 2px 8px rgba(37,99,235,0.3);
      z-index: 5;
    }
    /* 为了让不同标题视觉区分 */
    .section:nth-child(2) .sticky-title {
      background: #c2410c;
      border-bottom-color: #9a3412;
    }
    .section:nth-child(3) .sticky-title {
      background: #2b6f4b;
      border-bottom-color: #1e4b34;
    }
    .section p {
      padding: 14px 22px;
      margin: 0;
      border-bottom: 1px solid #e9edf3;
      font-size: 1.1rem;
    }
    .section p:last-child {
      border-bottom: none;
    }
    .note {
      margin-top: 30px;
      background: #fef9c3;
      border-left: 6px solid #eab308;
      padding: 16px 22px;
      border-radius: 24px;
      color: #854d0e;
    }
  </style>
</head>
<body>
<div class="card">
  <h2>📇 案例2 <span>sticky</span></h2>
  <div class="desc">🗂️ 粘性分区标题 --- 类似通讯录,当前标题固定,直到被下一个顶替。</div>

  <div class="scroll-sections">
    <div class="section">
      <h3 class="sticky-title">🌟 A 组 · 设计部</h3>
      <p>Alice Chen</p>
      <p>Amanda Li</p>
      <p>Alex Wang</p>
      <p>Angela Zhao</p>
      <p>Adam Brown</p>
    </div>
    <div class="section">
      <h3 class="sticky-title">🔥 B 组 · 市场部</h3>
      <p>Bob Zhang</p>
      <p>Bella Xu</p>
      <p>Ben Liu</p>
      <p>Bianca Wu</p>
      <p>Bruce Huang</p>
    </div>
    <div class="section">
      <h3 class="sticky-title">🌿 C 组 · 技术部</h3>
      <p>Chris Sun</p>
      <p>Ciara Zhou</p>
      <p>Carlos Lin</p>
      <p>Catherine Ma</p>
      <p>Clark Lee</p>
    </div>
  </div>

  <div class="note">
    💡 关键点:每个标题都是 <code>sticky</code>,后一个会把前一个顶上去,形成通讯录效果。父容器滚动区域必须足够高。
  </div>
</div>
</body>
</html>

效果:滚动时,当前章节的标题会固定在顶部,直到被下一个章节的标题推上去。这正是 iOS 通讯录常用的交互。


📁 案例三:粘性侧边栏

文件名建议sticky-sidebar-demo.html
效果:在双栏 Flex 布局中,左侧边栏滚动到指定位置后固定。

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>案例3:粘性侧边栏 · position: sticky</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: 'Segoe UI', Roboto, sans-serif;
      background: #e7edf5;
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
    }
    .card {
      max-width: 800px;
      width: 100%;
      background: white;
      border-radius: 40px;
      box-shadow: 0 30px 50px -20px rgba(0,30,50,0.25);
      padding: 30px;
    }
    h2 {
      font-weight: 550;
      color: #0f172a;
      margin-bottom: 6px;
      font-size: 2rem;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    h2 span {
      background: #2563eb;
      color: white;
      font-size: 0.9rem;
      padding: 4px 12px;
      border-radius: 30px;
    }
    .desc {
      color: #475569;
      margin-bottom: 30px;
      padding-left: 12px;
      border-left: 5px solid #2563eb;
      background: #f8fafc;
      padding: 12px 18px;
      border-radius: 20px;
    }
    /* flex 演示容器 */
    .demo-flex {
      display: flex;
      align-items: flex-start;  /* 防止拉伸,保证粘性有效 */
      gap: 20px;
      background: #ffffff;
      border-radius: 24px;
      border: 2px solid #ccdbe9;
      min-height: 380px;
    }
    .sidebar {
      position: sticky;
      top: 20px;                /* 距离父容器顶部20px时固定 */
      width: 180px;
      background: #dbeafe;
      padding: 30px 16px;
      border-radius: 18px;
      border: 2px solid #2563eb;
      color: #1e3a8a;
      font-weight: 600;
      text-align: center;
      margin: 18px 0 18px 18px;
      box-shadow: 0 8px 18px rgba(37,99,235,0.12);
    }
    .main-content {
      flex: 1;
      padding: 16px 20px 16px 0;
      height: 380px;
      overflow-y: auto;
    }
    .main-content p {
      background: #f1f5f9;
      padding: 16px 20px;
      border-radius: 16px;
      margin: 18px 0;
      border: 1px solid #dce3ef;
    }
    .note {
      margin-top: 30px;
      background: #fef9c3;
      border-left: 6px solid #eab308;
      padding: 16px 22px;
      border-radius: 24px;
      color: #854d0e;
    }
  </style>
</head>
<body>
<div class="card">
  <h2>🧩 案例3 <span>sticky</span></h2>
  <div class="desc">📌 粘性侧边栏 --- 在双栏布局中,侧边栏滚动到距离父容器顶部20px时固定。</div>

  <div class="demo-flex">
    <aside class="sidebar">
      ⚡ 侧边栏<br>
      <span style="font-size:0.9rem;">top:20px 固定</span>
    </aside>
    <div class="main-content">
      <p>📘 第1条:侧边栏会粘在距离父容器顶部20px的位置。</p>
      <p>📘 第2条:注意父容器要设置 align-items: flex-start。</p>
      <p>📘 第3条:并且不能有 overflow:hidden 限制。</p>
      <p>📘 第4条:增加内容方便滚动查看粘性效果。</p>
      <p>📘 第5条:直到父容器完全离开视口,侧边栏才会消失。</p>
      <p>📘 第6条:纯CSS实现,比JS方案更流畅。</p>
    </div>
  </div>

  <div class="note">
    💡 小提示:侧边栏的父容器(flex容器)必须有足够高度,并且侧边栏自身不能是 flex 拉伸的高度(使用 <code>align-self: flex-start</code> 或父级 <code>align-items: flex-start</code>)。
  </div>
</div>
</body>
</html>

注意 :要保证侧边栏的父容器(.container)有足够的高度,并且没有溢出隐藏。同时,侧边栏的兄弟元素(主内容)高度应大于侧边栏,否则侧边栏可能提前停止粘性。


这三个文件各自独立,展示了 position: sticky 最常见的应用场景。每个页面都包含了清晰的说明和可交互的滚动区域,非常适合作为技术博客的嵌入示例。读者可以分别打开,直观感受粘性定位的魅力。

4. 注意事项

  1. 父容器限制
    sticky 元素的活动范围仅限于其父容器内。当父容器滚出视口时,粘性元素也随之消失。
  2. 溢出属性
    如果父容器或任一祖先设置了 overflow: hidden / auto / scrollsticky 可能会失效(因为固定定位被限制在可滚动区域内)。通常建议不要在粘性元素的祖先上设置 overflowvisible
  3. 必须指定阈值
    至少指定 topbottomleftright 中的一个,否则行为类似 relative
  4. 多个粘性元素
    多个同向的粘性元素会依次堆叠,后一个会顶替前一个的位置(如实例二所示)。
  5. z-index
    需要时可以通过 z-index 控制堆叠顺序,避免被其他元素覆盖。
  6. 表格表头
    也可以用在 <thead> 上实现表头固定(但需注意浏览器兼容性)。

5. 浏览器兼容性

现代浏览器(Chrome、Firefox、Safari、Edge 较新版本)均支持 sticky。对于旧版浏览器(如 IE),可以使用 JavaScript 回退(例如用 position: fixed 结合滚动监听模拟),但通常 sticky 是渐进增强的特性,不强制支持。


6. 总结

position: sticky 让我们用纯 CSS 实现过去需要 JavaScript 才能完成的"滚动粘性"效果,代码简洁且性能更好。只需记住:一个粘性元素,一个阈值,一个不设溢出隐藏的父容器,就能轻松实现导航栏固定、分区标题、侧边栏跟随等常见交互。

如果这篇文章让你对CSS刮目相看,点个赞,转个发,收藏下,让更多朋友看到------CSS真的在吃掉前端。

#前端#CSS#干货

相关推荐
wordbaby2 小时前
前端进阶:小程序 Canvas 2D 终极指北 — 给图片优雅添加水印
前端·canvas
树上有只程序猿2 小时前
OpenClaw虽香,但不是人人都养得起“小龙虾
前端·openai
SuperEugene2 小时前
Vue3 + Element Plus 全局 Message、Notification 封装与规范|Vue生态精选
前端·javascript·vue.js
掘金安东尼2 小时前
活动落地页效率翻倍:RollCode 这次更新有点猛
前端·低代码·面试
北冥有鱼其名为坤2 小时前
诡异!vite+vue3 项目图片无法显示,我怀疑人生…
前端
FE_winter2 小时前
OpenClaw Skills 进阶实战:前端开发者的 AI 技能库搭建指南
前端·后端·程序员
wordbaby2 小时前
小白也能看懂:小程序 Canvas 给图片添加水印的终极指南
前端·canvas
Mapmost2 小时前
“汛”速响应:流域洪水仿真分析,如何实现淹没过程的精准推演?
前端
梁大虎2 小时前
Electrobun 开发必看:CEF 依赖下载失败?手动解压一招搞定!
前端·javascript·后端