在《Hexo博客优化教程:图库防盗链、回到顶部、悬挂喵功能修复》里,悬挂喵被改成内联脚本 + #szg-cat 的写法。之后又冒出两个新问题:全站都看不到这只喵,而且它一旦出现就会顶出页面底部。这篇把排查过程和最终修复记下来。
一、问题现象
- 每个页面右侧都看不到喵,但查看页面源码,
<div id="szg-cat">确实在。 - 从开发者工具里复制出来的元素长这样(
top是滚动后被 JS 改过的值):
1 | <div id="szg-cat" style="cursor: pointer; position: fixed; right: 80px; top: 0px; z-index: 9999; width: 70px; height: 900px; background: url("/medias/images/scroll.gif") no-repeat; transition: all 0.5s ease-in-out 0s; opacity: 1;"></div> |
height: 900px 加上 top: 0px,窗口高度不足 900px 时,底部自然就挤出屏幕了。
二、排查过程
1. 先确认元素是从哪来的
全仓库只有一处定义:
1 | grep -rn "szg-cat" --include="*.ejs" --include="*.html" \ |
layout.ejs 是主题外壳,第 19 行就是 <%- body %>,所有页面都从它出。也就是说这不是”某些页面漏了”,而是所有页面都没有。
2. 实测元素的实际尺寸
本地起一个静态服务器(绝对路径 /medias/... 在 file:// 下会解析到文件系统根目录,所以必须用 http):
1 | cd public && python3 -m http.server 8123 |
再用无头浏览器取计算样式:
1 | const el = document.getElementById('szg-cat'); |
高度是 0px。元素在 DOM 里,但没有任何尺寸,而且是透明的。
3. 为什么高度会是 0
#szg-cat 是个空 div,喵是它的 CSS 背景图。背景图不参与布局计算,空 div 的 height: auto 就是 0;背景永远画不出来。当时那份代码还额外写了 opacity: 0,要滚动超过 300px 才把它设成 1,于是短页面连触发机会都没有。
4. 那个 900px 是从哪来的
1 | sips -g pixelWidth -g pixelHeight public/medias/images/scroll.gif |
背景图本身就是 70×900。再看它外链的 szgotop.css:
1 | .back-to-top { /* ... */ height: 900px; /* ... */ } |
固定 900px 高,而且窗口宽度小于 1000px 时整个隐藏。对比一下 git 里的版本:
1 | git show HEAD:themes/hexo-theme-matery/layout/layout.ejs | grep szg-cat |
HEAD(也就是 CI 构建用的那版)是 top:-200px; height:900px,没有任何视口上限。滚动后 JS 把 top 改成 0,900px 的盒子就直接顶出视口底部。
三、原因归纳
| 现象 | 原因 |
|---|---|
| 全站都不显示 | 空 div + height:auto → 计算高度 0px;再叠加 opacity:0 + 滚动 300px 才显示 |
| 会超出页面底部 | 定位和高度写死 900px(沿用 szgotop.css 的值),没有视口上限;小于 1000px 宽时又被 CSS 整个隐藏 |
两个问题互相掩盖:一份代码里是”看得见但越界”,另一份是”不越界但根本看不见”。
四、修复
修改文件:themes/hexo-theme-matery/layout/layout.ejs
1 | <!-- 悬挂的喵 --> |
关键点:
height:900px保留一个基准值,空 div 必须有高度才能画出背景;max-height:calc(100vh - 20px)让 CSS 层先把底边兜住,即使 JS 没跑也不会越界。background-size:contain让整张 70×900 等比缩进盒子,喵画在这张图的底端,所以始终完整可见。top:0+center top保持”从页面顶端垂下来”的观感。- JS 里的
fitCat()按window.innerHeight - 20重算高度并监听resize,双保险。 - 去掉”滚动 300px 才显示”,
opacity:1直接可见;点击仍然回到顶部。
五、验证
1 | npx hexo generate |
600px 视口下重新实测同一个元素:
1 | vh=600 h=580px w=70px opacity=1 bgSize=contain top=0 bottom=580 越界=NO |
| 验证项 | 结果 |
|---|---|
| 站点生成 | 216 个文件 |
| 带新版标记的页面 | 212 / 220 |
| 600px 视口实测 | 高 580px,底边 580,不越界 |
| 首页 / 归档 / 分类 / 文章页抽检 | 均含新版 |
六、这些页面仍然不会有喵
| 页面 | 原因 |
|---|---|
box/index.html、box/404.html、box/cn/about.html、box/en/about.html |
拷贝进来的第三方项目,是 source/ 里的独立 HTML |
live2d-w/demo/demo.html、live2d-w/demo/login.html |
看板娘 demo 页 |
search/lyb.html |
front-matter 里写了 layout: "false",故意不走主题布局 |
jichou/index.html |
记仇表情包生成器,独立 HTML |
这些页面不经过主题的 layout.ejs,所以拿不到这个浮层。要在这些页面上也挂喵,得把同一段 div + script 直接写进对应文件里。
七、上线注意
- 改的是主题源码,必须重新生成并部署才会生效(
hexo clean && hexo g && hexo d,或等 CI 构建);CI 构建的是仓库里的提交内容,别忘了git push。 - 仓库根目录还留着一份更早的构建产物(
index.html、archives/等),用的是旧的.back-to-top类写法;而.github/workflows/deploy.yml是把仓库根目录整包当站点上传的。如果那份 Pages 也在提供访问,它不会跟着更新,建议把上传路径改成public。 szgotop.css现在已无实际用途(新构建里back-to-top/cd-top出现 0 次),可以删掉那个外链,少一个外部依赖。
附:本地复现与验证步骤
1 | # 1. 起静态服务器(必须在 public 目录下,用 http 而不是 file://) |
1 | const el = document.getElementById('szg-cat'); |
只要 h 不是 0、opacity 是 1、bottom 不超过 innerHeight,就说明喵既显示出来了,也没有跑出页面底部。
