功能需求
长文章中读者经常需要引用或分享某个具体章节。传统做法是手动查找标题的 id 再拼接 URL,效率很低。为每个标题自动添加锚点链接按钮,可以一键获取指向该章节的完整 URL。
核心实现
创建一个独立的 head-anchor.js 模块,负责动态注入锚点按钮:
1class HeadingAnchor {
2 constructor() {
3 this.headings = document.querySelectorAll(
4 '.post-content h2, .post-content h3, .post-content h4'
5 );
6 this.init();
7 }
8
9 init() {
10 this.headings.forEach(heading => {
11 const anchor = document.createElement('a');
12 anchor.className = 'heading-anchor';
13 anchor.innerHTML = '#';
14 anchor.href = `#${heading.id}`;
15 anchor.title = '复制链接';
16
17 anchor.addEventListener('click', (e) => {
18 e.preventDefault();
19 const url = `${location.origin}${location.pathname}#${heading.id}`;
20 navigator.clipboard.writeText(url).then(() => {
21 anchor.innerHTML = '✓';
22 anchor.classList.add('copied');
23 setTimeout(() => {
24 anchor.innerHTML = '#';
25 anchor.classList.remove('copied');
26 }, 1500);
27 });
28 });
29
30 heading.appendChild(anchor);
31 });
32 }
33}
34
35export default HeadingAnchor;
关键设计点
DOM 动态注入
不使用 Hugo 模板在编译期生成锚点 HTML,而是通过 JS 在客户端动态注入。这样做的优势是:
- 保持 Markdown 源文件的简洁性
- Hugo 的
Markdownify渲染出的标题在某些配置下可能不包含id,JS 注入更可控 - 方便与 SWUP 等页面切换库配合
剪贴板 API
使用 navigator.clipboard.writeText() 将完整 URL 写入剪贴板。相比传统的 document.execCommand('copy'),Clipboard API 是异步的、基于 Promise,且支持更精细的权限控制。
复制成功后锚点文字从 # 变为 ✓ 并添加 .copied 类,1.5 秒后恢复,提供清晰的操作反馈。
SWUP 兼容
SWUP 进行页面切换时 DOM 会被替换,需要在 SWUP 的内容替换事件中重新初始化:
1import HeadingAnchor from './head-anchor.js';
2
3document.addEventListener('swup:contentReplaced', () => {
4 new HeadingAnchor();
5});
6
7document.addEventListener('DOMContentLoaded', () => {
8 new HeadingAnchor();
9});
CSS 样式
锚点按钮默认隐藏,hover 标题时淡入显示:
1.heading-anchor {
2 display: inline-block;
3 margin-left: 8px;
4 font-size: 0.85em;
5 color: var(--color-primary);
6 opacity: 0;
7 transition: opacity 0.2s ease;
8 text-decoration: none;
9}
10
11h2:hover .heading-anchor,
12h3:hover .heading-anchor,
13h4:hover .heading-anchor {
14 opacity: 0.7;
15}
16
17.heading-anchor:hover {
18 opacity: 1 !important;
19}
20
21.heading-anchor.copied {
22 opacity: 1;
23 color: #22c55e;
24}
这样在不打扰正常阅读的前提下,需要时锚点自然浮现,交互完成后给予绿色✓的确认信号,整个流程简洁且优雅。
留言评论
期待你的想法评论加载中