
默认情况下,Typecho 的代码块不带复制功能,读者需要手动框选文字才能复制代码,体验较差。本文介绍一种纯前端方案——通过注入一段轻量级 JavaScript,在每个代码块右上角自动渲染"复制代码"按钮,无需安装插件,兼容大多数 Typecho 主题。
实现方式:纯 JavaScript + 动态注入 CSS,无外部依赖
适用场景:Typecho 博客,支持任意主题
一、注入脚本
1.1 选择注入位置
根据你使用的主题,选择以下任一方式将脚本添加到页面的 <head> 区域:
方式 A:直接编辑主题文件
找到主题目录下的 header.php,在 </head> 标签之前粘贴脚本代码。
方式 B:通过后台管理面板注入(推荐)
依次进入:控制台 → 外观 → 设置外观 → 主题自定义扩展
将代码粘贴到:
自定义 HTML 元素拓展 → 标签: head 头部(meta 元素后)
后台注入方式无需改动主题文件,升级主题时不会丢失配置,推荐优先使用。
1.2 完整脚本代码
将以下完整代码片段粘贴到上述位置:
<script>
// 在代码块右上角添加复制按钮
document.addEventListener('DOMContentLoaded', initCodeCopyButton);
function initCodeCopyButton() {
// 动态注入按钮样式
function initCSS(callback) {
const css = `
.btn-code-copy {
position: absolute;
line-height: .6em;
top: .5em;
right: .8em;
color: rgb(0, 205, 102);
font: menu;
}
.btn-code-copy:hover {
color: rgb(145, 145, 145);
cursor: pointer;
}
`;
const styleId = btoa('btn-code-copy').replace(/[=+\/]/g, '');
const head = document.getElementsByTagName('head')[0];
if (!head.querySelector('#' + styleId)) {
const style = document.createElement('style');
style.id = styleId;
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
}
callback();
}
// 执行复制操作(兼容旧版浏览器)
function copyTextContent(source) {
let result = false;
const target = document.createElement('pre');
target.style.opacity = '0';
target.textContent = source.textContent;
document.body.appendChild(target);
try {
const range = document.createRange();
range.selectNode(target);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand('copy');
window.getSelection().removeAllRanges();
result = true;
} catch (e) {
console.log('copy failed.');
}
document.body.removeChild(target);
return result;
}
// 为单个 <pre> 元素添加复制按钮
function initButton(pre) {
const code = pre.querySelector('code');
if (code) {
const preParent = pre.parentElement;
const newPreParent = document.createElement('div');
newPreParent.style = 'position: relative';
preParent.insertBefore(newPreParent, pre);
const copyBtn = document.createElement('div');
copyBtn.innerHTML = '复制代码';
copyBtn.className = 'btn-code-copy';
copyBtn.addEventListener('click', () => {
copyBtn.innerHTML = copyTextContent(code) ? '已复制' : '无法复制';
setTimeout(() => copyBtn.innerHTML = '复制代码', 250);
});
newPreParent.appendChild(copyBtn);
newPreParent.appendChild(pre);
}
}
// 查找页面中所有代码块并初始化
const pres = document.querySelectorAll('pre');
if (pres.length !== 0) {
initCSS(() => pres.forEach(pre => initButton(pre)));
}
}
</script>
1.3 脚本工作原理
脚本由三个核心函数组成,在页面加载完成后自动执行:
函数说明
| 函数 | 作用 |
|---|---|
initCSS() |
动态生成并注入按钮的 CSS 样式,通过唯一 ID 防止重复插入 |
copyTextContent() |
执行复制操作,兼容不支持 Clipboard API 的旧版浏览器 |
initButton() |
遍历页面中所有 <pre> 元素,为含有 <code> 子元素的代码块挂载按钮 |
按钮默认样式
| 样式属性 | 默认值 | 说明 |
|---|---|---|
| 正常颜色 | rgb(0, 205, 102) |
绿色,与常见代码主题搭配 |
| 悬停颜色 | rgb(145, 145, 145) |
灰色,提示可交互 |
| 位置 | 右上角(top: .5em; right: .8em) |
相对于代码块定位 |
| 反馈文案 | 复制代码 → 已复制 → 复制代码 | 点击后 250ms 内恢复原始文案 |
💡 自定义样式:如需调整按钮颜色或位置,修改脚本中
initCSS()函数内的css字符串即可,无需改动其他逻辑。
二、PJAX 兼容处理
2.1 为什么需要额外处理
如果你的主题开启了 PJAX(无刷新页面跳转),在用户点击链接切换页面时,浏览器不会触发完整的页面加载事件,导致 DOMContentLoaded 不再执行,新页面的代码块将不会自动挂载复制按钮。
2.2 配置 PJAX 回调
依次进入:控制台 → 外观 → 设置外观 → PJAX (BETA) → PJAX RELOAD
在回调列表中加入以下一行:
initCodeCopyButton();
这样每次 PJAX 完成页面切换后,都会重新执行初始化函数,确保新加载的代码块正常显示复制按钮。
⚠️ 注意:
initCodeCopyButton函数需要在全局作用域中可访问,即脚本不能被包裹在立即执行函数(IIFE)内。上方提供的代码已满足此要求。
参考资料
- 为 Typecho 代码块添加复制按钮 — logi.im
- Document.execCommand() — MDN Web Docs(已废弃但在旧版浏览器中仍广泛使用)
- Clipboard API — MDN Web Docs(现代浏览器推荐方案)