云的博客 logo 云的博客

知识分享 经验总结 技术前沿

  1. Home
  2. Posts
  3. 为Typecho代码块添加一键复制按钮

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

Jun 28, 2024 bayview

ChatGPT Image 2026年1月3日 14_44_24.png

默认情况下,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(现代浏览器推荐方案)

Table of Contents

  • 一、注入脚本
    • 1.1 选择注入位置
    • 1.2 完整脚本代码
    • 1.3 脚本工作原理
  • 二、PJAX 兼容处理
    • 2.1 为什么需要额外处理
    • 2.2 配置 PJAX 回调
  • 参考资料

Recent Posts

  • 用python演示A星算法 Feb 1, 2026
  • 手动部署 Mattermost 到 Ubuntu Server 20.04 Apr 17, 2025
  • 在 Ubuntu Server 上部署 FTP 服务 Jul 9, 2024
  • LFS安装笔记本无线网卡驱动需要注意的几点问题 Jun 30, 2024
  • 为Typecho代码块添加一键复制按钮 Jun 28, 2024
← 在Linux中删除EFI引导分区已弃用条目 LFS安装笔记本无线网卡驱动需要注意的几点问题 →
Powered by Hugo & Explore Theme.