position: sticky 本身不支持自动高亮——它仅控制定位,不感知滚动;高亮必须由 JavaScript 驱动,IntersectionObserver 是最轻量、现代且兼容性够用的方案。

position: sticky 本身不支持自动高亮跟踪
直接用 position: sticky 无法实现“滚动时自动高亮当前目录项”——它只控制元素是否粘在视口某处,不感知滚动位置、不触发样式变化。高亮跟踪是行为逻辑,必须配合 JavaScript 检测滚动节点击中点(intersection)或计算 offsetTop 差值来完成。
sticky + IntersectionObserver 实现节标题高亮
最轻量、现代且兼容性够用(Chrome 76+、Firefox 76+、Safari 15.4+)的做法:让目录项(<li> 或 <a>)作为锚点,用 IntersectionObserver 监听对应内容区块(如 <section id="xxx">)是否进入视口,并动态加 class。
- 目录项本身不需要
position: sticky,但它的容器(如侧边栏)需要设position: sticky; top: 1rem;才能随滚动固定显示 - 每个
<section id="features">需有唯一 ID,对应目录中<a href="#features"> - 观察器需配置
{ threshold: 0.1 },避免刚进视口就切换,更平滑 - 多个区块可能同时 intersect,建议取
entries.filter(e => e.isIntersecting)[0]选最靠上的一个
为什么不能只靠 top/bottom 偏移做“伪高亮”
有人试图给 sticky 元素加 top: 100px 再配合 transform: translateY() 模拟滚动跟随,但这本质是视觉欺骗,无法准确反映当前阅读章节。真实场景中:内容高度不均、有 margin/padding 折叠、响应式断点改变布局——这些都会让偏移计算失效。更关键的是,position: sticky 的生效前提是父容器可滚动且未设置 overflow: hidden;若目录树嵌在 overflow: auto 的小容器里,sticky 行为会被截断,高亮逻辑彻底失准。
目录树容器的 sticky 必须满足三个条件
否则 position: sticky 直接退化为 position: relative,根本不会“粘”:
立即学习“前端免费学习笔记(深入)”;
- 父级必须有明确的高度限制或滚动上下文(比如
max-height: 80vh; overflow-y: auto;) - 该父级不能是
overflow: hidden,否则 sticky 元素超出部分被裁剪,且失去粘性触发条件 - sticky 元素自身必须声明至少一个偏移方向(
top、bottom、left或right),例如top: 12px—— 缺少这个,浏览器直接忽略 sticky
最容易被忽略的是:当目录树放在 flex 容器里,而该 flex 容器又没设 align-items: flex-start,sticky 元素可能因基线对齐产生意外位移,导致粘性阈值错位。


















