为代码pre code元素添加一件复制功能

Jаvascript

为代码块添加一个"复制代码"按钮,并实现点击按钮后将代码块的内容复制到剪贴板中。

  1. 首先通过document.querySelectorAll('pre')获取所有<pre>元素(即代码块)。
  2. 使用forEach方法遍历每个代码块。
  3. 创建一个文本元素 copyButton,设置其class为"copy",并将显示文本设置为"复制代码"。
  4. 创建一个容器元素 container,设置其class为"code-container",并将复制按钮添加到容器元素内。
  5. 将容器元素插入到代码块之前。
  6. 设置容器元素样式,使其定位为相对定位(position: relative)。
  7. 设置复制按钮样式,使其绝对定位于容器元素的右上角。
  8. 为复制按钮添加点击事件监听器。
  9. 在点击事件处理函数中,获取代码块的文本内容。
  10. if (navigator.clipboard && window.isSecureContext)以此判断使用不同的复制方法,使用navigator.clipboard.writeText(code).then(() => {}).catch(() => {});或采用document.execCommand('copy'); 这里需要借助创建textarea
  11. 创建一个临时的 <textarea> 元素,并将代码块的内容设置为其值。
  12. <textarea> 元素追加到 <body> 中。
  13. 选中 <textarea> 中的文本。
  14. 执行复制操作,将选中的文本复制到剪贴板中。
  15. 移除临时的 <textarea> 元素。
  16. 修改复制按钮文本为"复制成功"。

这段代码的作用是为网页中的代码块添加一个复制按钮,方便复制代码片段。

CSS

用于设置复制按钮和代码块的样式。具体样式如下:

  1. .code-wrapper 类选择器用于设置包裹代码块的容器元素的样式。在这里设置了相对定位(position: relative)。
  2. .code-block 类选择器用于设置代码块的样式。在这里设置了相对定位(position: relative)。
  3. .copy 类选择器用于设置复制按钮的样式。具体样式如下:
    • font-size:设置字体大小为 13px。
    • transition:设置颜色变化的过渡效果为 0.1秒。
    • color:设置按钮的颜色为带透明度的红色color: hsl(165.03deg 19.14% 92.37% / 97%);,背景为灰色background: #1b45bede;。
    • border:去掉按钮的边框。
    • border-radius:设置按钮的圆角为4px。
    • cursor:设置鼠标悬停在按钮上时的样式为指针。
    • z-index:将复制按钮的层级置于顶层,确保按钮显示在其他内容之上。

这些样式可以使用在前面提到的 jаvascript 脚本中的相关元素上,以实现更好的外观和交互效果。

js文件,直接在html中引用 ,如果是在vue中使用,需要在渲染html的方法中使用下面的js。

javascript 复制代码
(function () {
  "use strict"
  // 获取<pre>元素
  var codeBlocks = document.querySelectorAll('pre');
  var codeContainer = document.querySelectorAll(".code-container");
  if (codeBlocks && codeContainer.length === 0) {
    codeBlocks.forEach(function (codeBlock) {
      // 创建新的ol元素
      // const ol = document.createElement('ol');
      // // 获取所有<code>标签中的文本行
      // const codeLines = codeBlock.textContent.split('\n');
      // // 移除<pre>中的所有内容
      // codeBlock.innerHTML = '';
      // // 为每行代码添加序号并重新添加到<pre>中
      // codeLines.forEach((line, index) => {
      //     const lineNumber = index + 1;
      //     const lineElement = document.createElement('li');
      //     lineElement.textContent = `${line}`;//${lineNumber}. 
      //     ol.appendChild(lineElement);
      //     // codeBlock.innerHTML = `<ol><li>${codeBlock.innerHTML.replace(/\n/g,`</li><li class="line">`)}</li></ol>`;
      // });
      // codeBlock.appendChild(ol);

      var copyButton = document.createElement('span');
      copyButton.className = 'copy';
      copyButton.textContent = '复制代码';

      // 创建包裹代码块和按钮的容器元素
      var container = document.createElement('div');
      container.className = 'code-container';

      // 将按钮添加到容器元素内
      container.appendChild(copyButton);

      // 将容器元素插入到代码块之前
      codeBlock.parentNode.insertBefore(container, codeBlock);

      // 设置容器元素样式,使其定位为相对定位(position: relative)
      container.style.position = 'relative';

      // 设置复制按钮样式,使其绝对定位于容器元素的右上角
      copyButton.style.position = 'absolute';
      copyButton.style.top = '3px';
      copyButton.style.right = '6px';

      copyButton.addEventListener('click', function () {
        // 获取代码块的文本内容textContent
        var code = codeBlock.innerText;

        if (navigator.clipboard && window.isSecureContext) {
          try {
            navigator.clipboard.writeText(code).then(() => {
              // 修改复制按钮文本为"已复制"
              this.textContent = '复制成功';
            }).catch(() => {
              this.textContent = '复制失败';
            });
          } catch (err) {
            this.textContent = '复制失败';
          }
        } else {
          // 创建一个临时的textarea元素,并将代码块的内容设置为其值
          var textarea = document.createElement('textarea');
          textarea.value = code;
          // 将textarea元素追加到body中
          document.body.appendChild(textarea);
          // 选中textarea中的文本
          textarea.select();
          // 执行复制操作
          document.execCommand('copy');
          // 移除临时的textarea元素
          document.body.removeChild(textarea);
          this.textContent = '复制成功';
        }
        //一定时间后吧按钮名改回来
        setTimeout(() => {
          this.textContent = "复制代码";
        }, 1800);
      });
    });
  }

  if (document.getElementById("copy-code-styles")) return; // 避免重复添加样式
  const css = `
  .code-wrapper {
    position: relative;
  }

  .code-block {
    position: relative;
  }

  .copy {
    font-size: 13px;
    transition: color 0.1s;
    color: hsl(165.03deg 19.14% 92.37% / 97%);
    background: #1b45bede;
    padding: 0 3px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    z-index: 1;
  }`;
  const style = document.createElement("style");
  style.id = "copy-code-styles";
  style.textContent = css;
  document.head.appendChild(style);
}());
相关推荐
LaoZhangAI22 分钟前
2025最全Supabase MCP使用指南:一键连接AI助手与数据库【实战教程】
前端·javascript·后端
江城开朗的豌豆34 分钟前
JavaScript篇:网页加载的玄机:DOMContentLoaded和load到底差在哪?
前端·javascript·面试
小公主38 分钟前
别再用 map(parseInt),我也是最近才发现问题出在哪
javascript
Huazi40 分钟前
利用 Scriptable 实现iOS 小组件实时查看网站访问数据
javascript
小猪Passion40 分钟前
🔥🔥🔥浅谈JavaScript闭包
前端·javascript·面试
xiaominlaopodaren43 分钟前
Three.js 教程:夜晚城市窗户发光的实现原理
前端·javascript
TimelessHaze1 小时前
为什么你总抢到几分钱?揭秘大厂常考的微信红包算法
前端·javascript·面试
bo521001 小时前
🔥 深度解析 IntersectionObserver API:从原理到实战(附懒加载完整代码)
前端·javascript
outstanding木槿1 小时前
现实生活例子[特殊字符] 通俗易懂的解释[特殊字符] JS中的原型和原型链[特殊字符]
前端·javascript·js
空中湖1 小时前
‘pnpm‘ 不是内部或外部命令,也不是可运行的程序
npm·node.js