
sticky定位为什么在侧边目录里经常失效
因为 position: sticky 不是“固定在视口”,而是“相对其最近的滚动祖先做粘性吸附”——如果父容器没设高度、没溢出、或设置了 transform/will-change,它就直接退化成 static。右侧目录常见失效场景:外层用了 display: flex 但没给主内容区设 min-height: 100vh,或者整个页面用 height: 100% 却没向上透传到 html 和 body。
实操建议:
- 确保滚动容器是
body或某个明确设了overflow-y: auto且有高度限制的祖先(比如.content-wrapper) - 侧边栏本身必须设
top: 20px(或具体偏移值),不能只写position: sticky - 避免对 sticky 元素或其任意父级使用
transform、perspective、filter(哪怕值是none) - Chrome 120+ 开始,
contain: paint也可能干扰 sticky 行为,慎加
右侧目录 sticky 的最小可行 CSS 结构
不需要 JS,也不依赖第三方库,纯 CSS 就能跑通,关键是层级和尺寸约束要闭环。下面这段是经过多端验证的最小可靠结构:
.article {
display: grid;
grid-template-columns: 1fr 280px;
min-height: 100vh;
}
.article-main {
overflow-y: auto;
max-height: calc(100vh - 60px); /* 避免底部被遮挡 */
}
.toc {
position: sticky;
top: 24px;
align-self: start;
height: fit-content;
}注意点:
立即学习“前端免费学习笔记(深入)”;
-
.article必须用grid或flex布局,不能靠float或绝对定位硬塞右侧 -
.toc的align-self: start是为了防止在 flex 容器中被拉伸(尤其 Safari) -
height: fit-content能阻止 Chrome 下 sticky 元素意外撑高容器导致滚动异常
滚动时目录链接高亮不同步怎么办
这是 sticky 和 JS 监听逻辑之间的典型错位:sticky 只管位置,不管语义;而锚点高亮依赖 IntersectionObserver 或 scroll 事件计算可视区域。当目录固定后,它自身不再滚动,但里面每个 <a href="#xxx"> 对应的标题仍在动。
关键处理逻辑:
- 监听的是
.article-main的滚动,不是window(否则在嵌套滚动容器里会失准) - 每个标题需有唯一
id,且IntersectionObserver的rootMargin要设成负值(如"-100px 0px -50% 0px"),提前触发高亮 - 不要用
offsetTop算位置——sticky 后元素的offsetTop不再随滚动变化 - 若用
scrollIntoView({ block: 'nearest' })跳转,记得加behavior: 'smooth',否则会破坏 sticky 的吸附节奏
移动端适配 sticky 侧边目录的三个硬约束
iOS Safari 对 sticky 支持较晚(iOS 14.5+ 才稳定),且在 viewport 缩放、横屏切换、键盘弹出等场景下容易脱钩。不加防护会直接“掉下来”。
必须做的三件事:
- 在
<head>加<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">,缺一不可 - 给
.toc加@supports (position: sticky) { ... }包裹,降级方案用 JS 模拟(比如监听scroll+getBoundingClientRect()) - 禁止给
.toc设置width: 100%或max-width百分比——移动端宽度波动会导致 sticky 重算失败,固定用px或rem
最麻烦的其实是 iOS 键盘唤起时,visualViewport 高度突变,sticky 元素会卡在半空。目前没有完美解法,只能监听 resize + focusin 事件强制重置 top 值。










