为Typecho代码块添加一键复制按钮

为Typecho代码块添加一键复制按钮

周五 6月 28 2024
1111 字 · 6 分钟
未分配分类 未分配标签

默认情况下,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)内。上方提供的代码已满足此要求。


参考资料


Thanks for reading!

为Typecho代码块添加一键复制按钮

周五 6月 28 2024
1111 字 · 6 分钟
未分配分类 未分配标签

© bayview | CC BY-NC-SA 4.0