回复带上引用:别再让读者猜「@昵称」在回哪一句

1268 字
6 分钟
回复带上引用:别再让读者猜「@昵称」在回哪一句
回复带上引用:别再让读者猜「@昵称」在回哪一句

友链页一条评论下面挂了十几条你来我往,读起来像在拼拼图:

@朝朝听雨:好简短的域名,是因为UK不能备案吗 @HY:uk是英国顶级域,备不了一点 @朝朝听雨:哈哈哈,我备案等了15天了

谁在回谁,一眼看得出;回的是对方哪句话,全靠猜。人一多、话一长,@昵称 就彻底不够用了 —— 你能定位到人,定位不到「他那句话里的哪一句」。

Waline 其实留了线索#

Waline 是 Vue 渲染的,DOM 里翻一圈,每条回复的正文第一行是这样的:

<p class="wl-reply-to">
<a href="#4508715">@朝朝听雨</a><span>: </span>
</p>

那个 href 是 #4508715 —— 被回复那条评论的 objectId。而页面里每条评论的节点,id 正好也是它:

<div id="4508715" class="wl-card-item">…</div>
<div id="4508716" class="wl-card-item">…</div>

也就是说,Waline 早就把「我在回哪一条」写在 DOM 里了,只是它自己没用(浏览器原生锚点跳转一跳就不知道跳哪去了,不如不用)。那我来用。

摘出「谁 + 说了什么」#

流程很短:拿到 href 里的 id → getElementById 找到目标评论 → 摘出昵称和正文 → 插一个引用块在 @昵称: 下面。

摘正文时有一件事必须做:把 @昵称: 那行前缀剥掉。因为目标本身可能也是一条回复,它自己的正文里也带着 回复 @某某:,直接取会让引用里再套一层 @,像回音。

// 只剥副本里的 .wl-reply-to(那是「回复 @某某:」前缀,不是他说的话)
var clone = content.cloneNode(true);
var prefix = clone.querySelector(".wl-reply-to");
if (prefix) prefix.remove();
var text = (clone.textContent || "").replace(/\s+/g, " ").trim();

cloneNode(true) 这个 true 是关键,下面会讲我漏了它的下场。

引用块本身是个 <button>,点了就滚过去并高亮:

target.scrollIntoView({ block: "center", behavior: "smooth" });
target.classList.remove(FLASH);
void target.offsetWidth; // 强制回流,让动画能连着重放
target.classList.add(FLASH);

void target.offsetWidth 那行是为了连点同一条时动画还能重放 —— 不强制一次回流,浏览器会把「移除类再立刻加回来」合并掉,第二次点就没反应了。

「@昵称:」下面多了一行引用块
「@昵称:」下面多了一行引用块

踩坑:忘了 clone,把页面上的东西删了#

第一版 quoteOf 是这么写的:

var prefix = content.querySelector(".wl-reply-to");
if (prefix) prefix.remove(); // ← content 是真实节点

content 是从真实 DOM 上取的,所以这行 remove() 删的不是副本,是页面上那行 @朝朝听雨: 本身。

而我要引用的正是它旁边的内容 —— 结果就是:每被引用一次,那条评论的 @昵称: 就消失一次。写的时候完全没意识到,直到跑完一看统计对不上:17 条回复插了 17 个引用块,页面上却只剩 8 个 .wl-reply-to。

这类 bug 的危险在于它不报错、页面也照常渲染 —— 只是少了点东西,而且少得很自然,不专门去数根本发现不了。

修法就一个字:cloneNode(true)。

和「回复折叠」串起来#

上一篇给回复做了折叠(默认展示 2 条,多出来的收起来)。这两个功能会撞车:

一条回复的引用块指向的目标,很可能正被折叠收着 —— 它 display: none,scrollIntoView 对它是没有任何反应的,点了就像没点一样。

所以折叠脚本那边开了个全局钩子:

window.__ffReplyCollapseExpand = function (item) {
var quote = item.parentElement;
if (!quote || !quote.classList.contains("wl-quote")) return;
var items = directItems(quote);
var index = items.indexOf(item);
if (index < 0) return;
if (index < Number(quote.dataset.ffShown || 0)) return;
apply(quote, Math.min(items.length, index + 1)); // 展开到这一条为止
};

引用脚本点下去之前先问一句:

if (target.classList.contains("ff-rp-hidden")) {
window.__ffReplyCollapseExpand(target);
}

两个脚本各自独立加载、互不依赖,钩子没挂上就退化成「只滚不展开」,不会报错。展开后折叠按钮的计数也会跟着更新(实测:默认 2 条时点一个指向第 4 条的引用,会展开到 4 条,按钮同步变成「展开更多(还剩 9 条)」)。

边界#

情况处理
目标不在当前页(分页翻走了、评论被删)找不到节点,静默跳过,不插引用块,Waline 原样显示
目标是顶层评论正常引用
目标本身也是回复取其正文,但先剥掉它自己的 @ 前缀,避免套娃
目标被折叠收着点引用时先自动展开到那一条
引用的是自己跳过(target === item)
正文太长摘 120 字,CSS 再截到两行 —— 引用是提示「在回哪句」,不该跟正文抢长度

考虑到本机首页贴纸、相册那些页面的经验,取子元素我一律走直接子而不是 querySelector:一条顶层评论的 DOM 里还嵌着十几条回复,item.querySelector('.wl-content') 捞到谁都全看文档顺序。同一个道理,判断「这条是不是已经加过引用块」也要看直接子,不然会把自己递归判断成「已经处理过」。

实测友链页:17 条回复全部带上引用块,@昵称: 一行不少、位置也对(引用块紧跟在它后面),点引用能滚到原评论并高亮。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
回复带上引用:别再让读者猜「@昵称」在回哪一句
https://www.9ll.uk/posts/blog/commentreplyquote/
作者
HY
发布于
2026-10-15
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
评论区回复默认展开 2 条,长串收在「展开更多」后面
博客友链页有条评论下面挂了 13 条回复,往下翻要滑过整屏;可全收起来又太狠,访客第一眼什么都看不到。最后定成默认展示 2 条,多出来的才收着,还有剩就给个「展开更多」,摊完能收回去。做法照旧只加文件不动上游。踩坑集中在一点上:Waline 是 Vue 渲染的,DOM 会重绘、会随 swup 换页被整体替换,所以折叠状态不能只靠一次性的 DOM 操作。
2
全站加了个「最近评论」:数据直连 Waline,主题文件一个没改
博客在参考站的右栏看到「最近评论」组件,想在自己站上也挂一个,而且要求全站都有。评论数据就在自己的 Waline 后端里,直接调它的公开接口;DOM 和样式走页脚注入,脚本全站加载,桌面挂右栏、移动端挂底部组件栏。踩了三个坑:Waline 的 orig 字段是渲染前的 HTML、类名和外包装撞车吃掉 32px 内边距、窄栏 flex 布局把昵称挤成一个字。
3
全站鼠标指针换成《俺妹》主题:亮色桐乃、深色黑猫
博客这次更新把整站的鼠标指针换了一遍:亮色主题配高坂桐乃、深色主题配五更琉璃(黑猫),各 28 枚共 56 枚,正常选择、链接、文本、禁用、拖拽这些场景全部覆盖。素材来自两张总览图,切图时发现它们不是普通白底图 —— 人物衣服和箭头内部都是纯白,跟我页面的白同色,按我处理贴纸那套「全局去白底」会把图形整个掏空。CSS 那边我把「用哪张图 + 热点坐标」全塞进了自定义属性,28 条规则只写一遍,切换主题只换一组变量。
4
把动态页改成了朋友圈的样子
博客动态页想换个样子,参照的是另一个同主题的站 —— 但数据源不变,还是继续接 memos。扒完两边才发现,上游动态卡片的 DOM 本来就齐 —— 头像、作者、时间、地点、置顶徽章、图片网格、评论按钮一个不缺,差的只是样式。于是这轮改造零改动主题源文件:一份只在动态页加载的样式表,加一个把页脚外壳搬进正文的小脚本。中间踩了三个安静的坑,都是「不报错、但就是不生效」那种。
5
全站加了个「天气预报」:接口免费,而且按访客 IP 定位
博客刷参考站时看到他右栏挂着一张天气预报卡,从标题竖条到「收起」按钮整个搬了过来。接口在 uapis.cn,免费、不用 key,而且不传参数时按访客 IP 定位 —— 谁来串门看到的就是谁头顶上的天。踩了两个坑:参考站那套图标是 Astro 构建期渲染的 symbol、动态插进去取不到;指标网格的 auto-fit 在 280px 右栏只排得出一列,卡片被顶到 925px。
随机文章随机推荐

评论区

Profile Image of the Author
HY
Hello, I'm HY.
公告
迷茫.... 为了访问速度,本站将会在11月上旬至中旬迁移到0dm.cn并完成备案
分类
标签
最新动态
站点统计
文章
74
分类
8
标签
192
总字数
161,979
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录