背景:给 PaperMod 博客做「左侧悬浮目录」,在做窄屏适配时,发现
position: fixed方案越补越复杂,进而推导出position: sticky才是正解。
一、问题的本质:两个"参照系"打架
当前目录用的是 position: fixed,正文是居中布局。这导致了根本矛盾:
| 元素 | 定位方式 | 参照系 |
|---|---|---|
目录 .toc-sidebar | position: fixed | 整个浏览器窗口(屏幕) |
正文 .post-content | 居中 + max-width | 屏幕正中央的固定宽度容器 |
两个元素各自用不同的参照系定位,所以:
- 屏幕很宽时,两者刚好不重叠(纯属运气)
- 屏幕变窄时,正文的"左右留白"缩小,目录(相对屏幕固定)就会压到正文
结论:用 fixed 就注定要"打补丁"——写一堆媒体查询去"猜"什么宽度下会重叠。这是治标不治本。
二、为什么"缩小距离/缩小宽度/隐藏"是补丁,不是解法
一开始设想的"三步判断"逻辑:
- 屏幕够宽 → 目录浮在正文旁
- 二者贴在一起 → 缩小目录宽度
- 还不行 → 隐藏目录
这个逻辑方向是对的,但用媒体查询实现时,本质是在**“手动模拟"本应由浏览器自动完成的事**:
- 你得猜“多少像素下会重叠”(是 1200?1300?1400?)
- 每猜一次,就要加一个
@media断点 - 屏幕尺寸无穷多,断点也写不完
- 换个设备、换个分辨率,又得重调
这就是"补丁式"响应式的通病:永远在追赶,永远有漏网之鱼。
三、position: sticky 为什么是正解
sticky 解决的是**“参照系统一”**这个根本问题。
fixed 和 sticky 的本质区别
| 属性 | 定位参照系 | 表现 |
|---|---|---|
position: fixed | 相对屏幕 | 永远钉在屏幕某个位置,与页面内容无关 |
position: sticky | 相对父容器 | 在父容器范围内"跟随滚动”,超出范围就正常流动 |
sticky 如何从根上避免"重合"
如果目录用 sticky,并且放在正文容器的左侧(作为正文布局的一部分),那么:
- 目录的位置是相对于正文容器计算的,不再是"相对屏幕"
- 正文容器居中、目录贴着它左侧 → 目录天然永远贴着正文,永远不会重叠
- 屏幕变窄时,整个"正文+目录"一起居中缩小,目录自动跟着正文走
这就是"从结构上避免重叠",而不是"靠一堆断点去补救重叠"。
四、一个直观的类比
position: fixed:像在墙上钉了个钉子,挂了个相框。墙(屏幕)多大,钉子就钉在离墙边固定的位置,跟家具(正文)没关系。家具挪了,相框还在原地,可能挡住家具。position: sticky:像把相框贴在某个柜子侧面。柜子(正文容器)挪到哪,相框跟着到哪,永远不会和柜子里的东西打架。
五、sticky 的代价(诚实说明)
sticky 不是零成本,它有个前提:
- 需要调整 HTML 结构:目录必须是"正文容器"的子元素(或与正文在同一个相对定位的容器里),才能以正文为参照。当前的
single.html结构是"目录和正文平级",需要先改造。 - 需要理解 flex + sticky 的配合:通常用"左栏 sticky + 右栏正文"的两栏结构实现。
所以它的学习成本略高,但换来的是"一次做对、以后不用打补丁"。
六、结论
| 方案 | 优点 | 缺点 | 适用 |
|---|---|---|---|
fixed + 媒体查询补丁 | 改动小、好理解 | 治标不治本,断点写不完 | 临时过渡、快速见效 |
sticky 锚定正文 | 从根上避免重叠,一劳永逸 | 要调 HTML 结构,稍复杂 | 最终方案、长期维护 |
最终结论:position: fixed 适合"先快速上线看效果",position: sticky 才是"窄屏适配 + 跟随滚动"的正解。目标是从 fixed 迁移到 sticky。
七、速记关键词
- 参照系(positioning context):
fixed相对屏幕,sticky相对父容器。 - 补丁式响应式:靠一堆
@media断点"猜"重叠点,永远追不完。 - 结构性解法:让目录成为正文布局的一部分,从根上避免重叠。
- sticky 的前提:目录要放在正文容器的左侧,作为子元素或同一相对定位容器内。