为你的 Hexo 博客添加交互式点击提示:clicktip 插件详解

在博客写作中,我们经常需要对某些词语或概念进行补充说明。传统的做法要么是使用括号注释打断阅读 flow,要么是使用脚注需要读者跳转查看。今天我要介绍一款自己开发的 Hexo 插件 ——clicktip,它能让你的读者通过点击词语就能查看详细说明,既保持了文章的简洁性,又提供了丰富的补充信息。

什么是 clicktip 插件?

clicktip 是一款专为 Hexo 博客设计的标签插件,它允许你在文章中为任意词语添加点击提示功能。当读者点击带有提示的词语时,会显示预设的解释内容,再次点击或点击其他地方则会关闭提示。这种交互方式既直观又不影响正常阅读,非常适合添加注释、解释专业术语或提供额外背景信息。

插件特点

-简洁易用:通过简单的标签语法即可为词语添加提示功能
-样式丰富:支持多种触发元素样式(下划线、虚线等)
-交互友好:提供高亮、颜色变化等悬停效果
-智能定位:提示框会根据页面空间自动选择最佳显示位置
-响应式设计:在不同屏幕尺寸下都能良好展示
-无冲突保证:通过唯一 ID 机制确保页面中多个提示框正常工作

安装方法

安装 clicktip 插件非常简单:
1.在你的 Hexo 博客根目录下,创建 scripts 文件夹(如果已存在则忽略)
2.在 scripts 文件夹中创建 clicktip.js 文件,并将插件代码复制进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// 计数器确保ID唯一性
let clickTipCounter = 0;

hexo.extend.tag.register('clt', function(args) {
const word = args[0];
const tip = args[1].replace(/"/g, '');
const styleType = args[2] || 'none';
const hoverEffect = args[3] || 'none'; // 新增:悬停效果参数
const hoverColor = args[4] || '#2c3e50'; // 新增:悬停颜色参数

// 使用递增计数器生成唯一ID
const uniqueId = `click-tip-${Date.now()}-${clickTipCounter++}`;

// 设置触发元素的类
let triggerClasses = 'click-tip-trigger';
if (styleType === 'underline') triggerClasses += ' click-tip-underline';
if (styleType === 'dashed') triggerClasses += ' click-tip-dashed';
if (hoverEffect === 'highlight') triggerClasses += ' click-tip-highlight';
if (hoverEffect === 'color') triggerClasses += ' click-tip-color';

// 添加自定义数据属性
const dataAttrs = hoverEffect === 'color' ? `data-hover-color="${hoverColor}"` : '';

return `
<span class="click-tip-wrapper" data-id="${uniqueId}">
<span id="${uniqueId}-trigger" class="${triggerClasses}" ${dataAttrs}>
${word}
</span>
<span id="${uniqueId}-content" class="click-tip-content">
${tip}
</span>
</span>
`;
});

// 注册过滤器,在HTML渲染后添加统一的样式和脚本
hexo.extend.filter.register('after_render:html', function(html) {
const script = `
<style>
.click-tip-wrapper { position: relative; display: inline-block; }
.click-tip-trigger { cursor: pointer; color: #2c3e50; transition: all 0.2s; }
.click-tip-underline { text-decoration: underline; }
.click-tip-dashed { border-bottom: 1px dashed #999; }
.click-tip-highlight:hover { background-color: rgba(0,0,0,0.05); }
.click-tip-color:hover { color: var(--hover-color, #3498db); }

.click-tip-content {
display: none;
position: absolute;
padding: 8px 12px;
background: #fff;
border: 1px solid #eaecef;
border-radius: 4px;
font-size: 0.9em;
color: #333;
white-space: nowrap;
z-index: 100;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
opacity: 0;
transform: translateY(8px);
transition: opacity 0.2s ease, transform 0.2s ease;
}

.click-tip-content.active {
display: block;
opacity: 1;
transform: translateY(0);
}

/* 响应式定位样式 */
.click-tip-content.position-top { top: auto; bottom: 100%; margin-bottom: 8px; }
.click-tip-content.position-right { left: 100%; top: 0; margin-left: 8px; }
.click-tip-content.position-left { left: auto; right: 100%; top: 0; margin-right: 8px; }
</style>

<script>
document.addEventListener('DOMContentLoaded', function() {
// 设置颜色悬停效果
document.querySelectorAll('.click-tip-color').forEach(el => {
const hoverColor = el.dataset.hoverColor || '#3498db';
el.style.setProperty('--hover-color', hoverColor);
});

// 事件委托处理所有点击
document.body.addEventListener('click', function(e) {
const trigger = e.target.closest('.click-tip-trigger');
if (!trigger) return;

e.stopPropagation();
const wrapper = trigger.closest('.click-tip-wrapper');
const content = wrapper.querySelector('.click-tip-content');
const isActive = content.classList.contains('active');

// 关闭所有提示框
document.querySelectorAll('.click-tip-content.active').forEach(el => {
el.classList.remove('active');
});

// 切换当前提示框
if (!isActive) {
positionTooltip(wrapper);
content.classList.add('active');
}
});

// 点击外部关闭提示框
document.addEventListener('click', function() {
document.querySelectorAll('.click-tip-content.active').forEach(el => {
el.classList.remove('active');
});
});

// 智能定位提示框
function positionTooltip(wrapper) {
const content = wrapper.querySelector('.click-tip-content');
const trigger = wrapper.querySelector('.click-tip-trigger');

// 重置位置
content.className = 'click-tip-content';

// 获取视口尺寸和元素位置
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const { top, left, width, height } = trigger.getBoundingClientRect();
const tipWidth = content.offsetWidth;
const tipHeight = content.offsetHeight;

// 计算可用空间
const spaceRight = viewportWidth - (left + width);
const spaceLeft = left;
const spaceBelow = viewportHeight - (top + height);
const spaceAbove = top;

// 智能定位逻辑
if (spaceBelow >= tipHeight) {
// 默认下方
content.style.top = 'calc(100% + 8px)';
content.style.left = '0';
} else if (spaceAbove >= tipHeight) {
// 上方
content.classList.add('position-top');
content.style.left = '0';
} else if (spaceRight >= tipWidth) {
// 右侧
content.classList.add('position-right');
content.style.top = '0';
} else if (spaceLeft >= tipWidth) {
// 左侧
content.classList.add('position-left');
content.style.top = '0';
} else {
// 缩小并居中显示
content.style.whiteSpace = 'normal';
content.style.maxWidth = '200px';
content.style.top = 'calc(100% + 8px)';
content.style.left = '50%';
content.style.transform = 'translateX(-50%)';
}
}

// 窗口大小改变时重新定位
window.addEventListener('resize', function() {
document.querySelectorAll('.click-tip-content.active').forEach(content => {
const wrapper = content.closest('.click-tip-wrapper');
positionTooltip(wrapper);
});
});
});
</script>
`;

// 将脚本和样式添加到页面底部
return html.replace('</body>', script + '</body>');
});

3.清理 Hexo 生成的静态文件和缓存

1
hexo clean

使用方法

clicktip 插件提供了简洁的标签语法:

1
{% clt 词语 "提示内容" [样式类型] [悬停效果] [悬停颜色] %}

标签可改:

1
2
>代码第四行:
>hexo.extend.tag.register('clt', function(args){...});

参数说明:

-词语:需要添加提示的词语或短语
-提示内容:点击后显示的提示文本(需要用双引号包裹)
-样式类型(可选):
-none:无特殊样式(默认)
-underline:下划线样式
-dashed:虚线样式
-悬停效果(可选):
-none:无效果(默认)
-highlight:背景高亮效果
-color:文字变色效果
-悬停颜色(可选):当悬停效果为 color 时生效,可指定颜色值(默认 #2c3e50)

示例:

1
{% clt "提示" "这是一个提示" underline color #ff0000 %}
提示 这是一个提示