position: sticky 是首选方案,因其原生支持滚动跟随、性能好、语义清晰且兼容性足够(chrome 56+、firefox 59+、safari 15.4+),关键在于“在父容器内相对滚动粘滞”而非固定视口。

为什么 position: sticky 是首选方案
因为它是原生支持滚动跟随的定位方式,无需 JS 监听 scroll 事件,性能好、语义清晰、兼容性已足够(Chrome 56+、Firefox 59+、Safari 15.4+)。关键不是“固定在视口”,而是“在父容器内相对滚动粘滞”——这点常被误解。
常见错误是直接给目录容器设 position: sticky 却没限制父容器高度或溢出行为,导致粘滞失效。它必须在一个有明确滚动边界(比如 overflow-y: auto)且高度受限的父容器中才能触发。
- 父容器需设置
max-height或明确高度,且overflow-y: auto -
top值决定粘滞触发位置(例如top: 1rem表示距父容器顶部 1rem 时开始吸附) - 不能和
float、clear同时使用;父容器不能是transform、perspective或filter非 none 的元素(否则形成新的 stacking context,sticky 失效)
如何让目录项高亮对应当前滚动章节
纯 CSS 无法主动判断“哪一节在视口中心”,但可以用 :target 配合锚点跳转做被动高亮;若要自动跟随,必须用 JS 检测 getBoundingClientRect() 或 IntersectionObserver。后者更推荐——轻量、异步、不阻塞渲染。
典型做法:为每个标题添加 id(如 <h2 id="section-1"></h2>),监听其进入视口状态,动态给对应目录项加 class="active",再用 CSS 设置样式。
-
IntersectionObserver的threshold建议设为[0.1],避免因标题过短导致错过触发 - 目录项
<a href="#section-1"></a>必须与标题id严格一致,大小写、连字符都不能错 - 不要用
scrollIntoView({ behavior: 'smooth' })配合 active 切换,否则滚动和高亮不同步;应先更新 active,再滚动
侧边栏宽度与响应式断点怎么设才不打架
固定侧边栏容易在小屏上挤占正文空间,硬设 width: 240px 在手机上会直接撑破布局。正确做法是分层控制:用 min-width 限定最小可用宽度,用媒体查询在窄屏下关闭侧边栏或改为底部折叠面板。
注意:不要只靠 @media (max-width) 隐藏,还要同步移除 position: sticky 相关样式,否则可能残留空白占位。
- PC 端建议
width: 220px~260px,留出gap或padding避免文字贴边 - 平板(~768px)可降为
width: 180px,字体缩小一级 - 手机(
max-width: 600px)建议用display: none隐藏侧边栏,改用顶部按钮触发抽屉式目录 - 如果正文用了
grid布局,侧边栏应作为独立grid-column,而非浮动元素,否则响应式重排易错乱
滚动卡顿或目录跳动的三个隐藏原因
看似简单的 sticky 目录,实际运行中常出现滚动抖动、active 错位、甚至整个侧边栏突然消失——多数不是代码写错,而是底层渲染机制被意外触发。
- 父容器设置了
will-change: transform或backface-visibility: hidden,会导致 sticky 定位计算异常,删掉即可 - 目录项内部用了
flex+flex-wrap: wrap,当文字换行时高度突变,引发 sticky 重计算并视觉跳动;改用display: block或固定行高可缓解 - 页面存在
font-display: swap字体加载,首屏渲染后字体回填导致 DOM 高度变化,进而影响 IntersectionObserver 判断;可在字体加载完成后再初始化 observer
最稳妥的调试方式:打开 Chrome DevTools → Rendering → 勾选 “Layout Shift Regions”,看哪些区域在滚动中反复重排。真正稳定的目录跟随,往往是在删掉一堆“看起来很酷”的优化之后实现的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











