背景:给 PaperMod 博客做「左侧悬浮目录」,在做窄屏适配时,发现 position: fixed 方案越补越复杂,进而推导出 position: sticky 才是正解。


一、问题的本质:两个"参照系"打架

当前目录用的是 position: fixed,正文是居中布局。这导致了根本矛盾:

元素定位方式参照系
目录 .toc-sidebarposition: fixed整个浏览器窗口(屏幕)
正文 .post-content居中 + max-width屏幕正中央的固定宽度容器

两个元素各自用不同的参照系定位,所以:

  • 屏幕很宽时,两者刚好不重叠(纯属运气)
  • 屏幕变窄时,正文的"左右留白"缩小,目录(相对屏幕固定)就会压到正文

结论:用 fixed 就注定要"打补丁"——写一堆媒体查询去"猜"什么宽度下会重叠。这是治标不治本。


二、为什么"缩小距离/缩小宽度/隐藏"是补丁,不是解法

一开始设想的"三步判断"逻辑:

  1. 屏幕够宽 → 目录浮在正文旁
  2. 二者贴在一起 → 缩小目录宽度
  3. 还不行 → 隐藏目录

这个逻辑方向是对的,但用媒体查询实现时,本质是在**“手动模拟"本应由浏览器自动完成的事**:

  • 你得“多少像素下会重叠”(是 1200?1300?1400?)
  • 每猜一次,就要加一个 @media 断点
  • 屏幕尺寸无穷多,断点也写不完
  • 换个设备、换个分辨率,又得重调

这就是"补丁式"响应式的通病:永远在追赶,永远有漏网之鱼。


三、position: sticky 为什么是正解

sticky 解决的是**“参照系统一”**这个根本问题。

fixed 和 sticky 的本质区别

属性定位参照系表现
position: fixed相对屏幕永远钉在屏幕某个位置,与页面内容无关
position: sticky相对父容器在父容器范围内"跟随滚动”,超出范围就正常流动

sticky 如何从根上避免"重合"

如果目录用 sticky,并且放在正文容器的左侧(作为正文布局的一部分),那么:

  • 目录的位置是相对于正文容器计算的,不再是"相对屏幕"
  • 正文容器居中、目录贴着它左侧 → 目录天然永远贴着正文,永远不会重叠
  • 屏幕变窄时,整个"正文+目录"一起居中缩小,目录自动跟着正文走

这就是"从结构上避免重叠",而不是"靠一堆断点去补救重叠"。


四、一个直观的类比

  • position: fixed:像在墙上钉了个钉子,挂了个相框。墙(屏幕)多大,钉子就钉在离墙边固定的位置,跟家具(正文)没关系。家具挪了,相框还在原地,可能挡住家具。

  • position: sticky:像把相框贴在某个柜子侧面。柜子(正文容器)挪到哪,相框跟着到哪,永远不会和柜子里的东西打架。


五、sticky 的代价(诚实说明)

sticky 不是零成本,它有个前提:

  1. 需要调整 HTML 结构:目录必须是"正文容器"的子元素(或与正文在同一个相对定位的容器里),才能以正文为参照。当前的 single.html 结构是"目录和正文平级",需要先改造。
  2. 需要理解 flex + sticky 的配合:通常用"左栏 sticky + 右栏正文"的两栏结构实现。

所以它的学习成本略高,但换来的是"一次做对、以后不用打补丁"。


六、结论

方案优点缺点适用
fixed + 媒体查询补丁改动小、好理解治标不治本,断点写不完临时过渡、快速见效
sticky 锚定正文从根上避免重叠,一劳永逸要调 HTML 结构,稍复杂最终方案、长期维护

最终结论position: fixed 适合"先快速上线看效果",position: sticky 才是"窄屏适配 + 跟随滚动"的正解。目标是从 fixed 迁移到 sticky


七、速记关键词

  • 参照系(positioning context)fixed 相对屏幕,sticky 相对父容器。
  • 补丁式响应式:靠一堆 @media 断点"猜"重叠点,永远追不完。
  • 结构性解法:让目录成为正文布局的一部分,从根上避免重叠。
  • sticky 的前提:目录要放在正文容器的左侧,作为子元素或同一相对定位容器内。