为代码pre code等显示区域添加一件复制功能

11 篇文章 1 订阅
4 篇文章 0 订阅

在代码pre标签前面插入复制按钮

js代码,这里比较简单直接在pre标签前插入一个code-tool-container,新增悬停显示复制按钮:

(function () {
  "use strict"
  //在这里使用需要判断样式是否不存在,不存在则开始添加到head中,避免了使用return导致函数出现终止执行问题
  if (!document.getElementById("copy-code-styles")) {
    //这里就是添加css样式表到head的主要代码
    const css =
      `
      /* 设置容器元素样式,使其定位为相对定位(position: relative) */
      .code-tool-container {
      position: relative;
      }

      .code-tool-container.ctc-hidden {
      display: none;
      }
      .copy-btn {
      font-size: 13px;
      transition: color 0.1s;
      color: hsl(9.96deg 88.32% 47.38% / 97%);
      background: #dfe8e6;
      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);
  }

  // 获取<pre>元素
  const codeBlocks = document.querySelectorAll('pre');
  const toolContainers = document.querySelectorAll(".code-tool-container");
  if (codeBlocks) {
    codeBlocks.forEach(function (codeBlock, cbIndex) {
      //这段注释的代码是给每一行添加行数,便于代码阅读和显示
      //判断当前代码块不存在行数显示标签ol时,才创建新的ol
      // if (!codeBlock.querySelectorAll('ol')) {
      //   // 创建新的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.appendChild(ol);
      // }

      //判断当前代码块的索引序号是否小于或等于复制按钮容器数,为true表示没有新的代码块显示,返回终止函数
      if (toolContainers && cbIndex + 1 <= toolContainers.length) {
        return;
      }

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

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

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

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

      // 设置复制按钮样式,使其绝对定位于容器元素的右上角
      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);
      });

      //实现代码块pre悬停显示复制按钮等,包括桌面和移动端
      // 监听mouseenter和mouseleave事件
      codeBlock.addEventListener('mouseenter', (e) => {
        // 鼠标进入元素时的效果
        toolContainer.classList.remove('ctc-hidden');
        toolContainer.addEventListener("mouseenter", (e) => {
          e.target.classList.remove('ctc-hidden');
        })
      });

      codeBlock.addEventListener('mouseleave', (e) => {
        // 鼠标离开元素时的效果
        toolContainer.classList.add('ctc-hidden');
        toolContainer.addEventListener("mouseleave", (e) => {
          e.target.classList.add('ctc-hidden');
        })
      });

      // 为了适配触摸设备,可以监听触摸事件
      codeBlock.parentElement.addEventListener('touchstart', function (e) {
        // 阻止默认的触摸行为,例如缩放
        //e.preventDefault();
        if (codeBlock.contains(e.target) || toolContainer.contains(e.target)) {
          // 触摸开始时的效果
          toolContainer.classList.remove('ctc-hidden');
        }
      });

      // 为了适配触摸设备,可以监听页面,当任意地方按下或触摸时
      document.addEventListener("touchstart", (e) => {
        // 任意点击不是当前pre的位置
        if (!codeBlock.contains(e.target) && !toolContainer.contains(e.target)) {
          toolContainer.classList.add('ctc-hidden');
        }
      });
    });
  }

  // if (document.getElementById("copy-code-styles")) return; // 避免重复添加样式
  //如果要使用这种方法,需要把上面的判断样式不存在中的添加css的代码转移到下面,
  //因为return;会终止当前函数,也就是下面的不会再执行了,所以添加到head的方法移到下面
}());

在代码块pre中添加一个容器,里面包含复制按钮

这里是在pre中添加一个容器,里面放置一键复制按钮,主要可以实现更多的定制化,对于一键复制和其他功能可以更加自由扩展,需要在pre上添加position: relative;

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。

(function () {
  //在这里使用需要判断样式是否不存在,不存在则开始添加到head中,避免了使用return导致函数出现终止执行问题
  if (!document.getElementById("copy-code-styles")) {
    //这里就是添加css样式表到head的主要代码
    const css = `
    .code-wrapper {
    position: relative;
    }

    .code-block {
    position: relative;
    }

    .copy {
    font-size: 13px;
    transition: color 0.1s;
    color: hsl(9.96deg 88.32% 47.38% / 97%);
    background: #dfe8e6;
    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);
  }

  // 获取<pre>元素
  const codeBlocks = document.querySelectorAll('pre');
  //获取复制按钮所在容器
  const codeContainer = document.querySelectorAll(".code-container");
  if (codeBlocks) {
    codeBlocks.forEach(function (codeBlock) {
      //这段注释的代码是给每一行添加行数,便于阅读
      //判断当前代码块不存在行数显示标签ol时,才创建新的ol
      // if (!codeBlock.querySelectorAll('ol')) {
      //   // 创建新的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.appendChild(ol);
      // }

      //判断当前代码块的索引序号是否小于或等于复制按钮容器数,为true表示没有新的代码块显示,返回终止函数
      //这里使用的原因是我的应用场景涉及到html动态变化,会出现新增的代码块
      //一般情况是在文章页面加载完成调用一次,而文章显示页面不会出现动态修改的问题,所以可以不用使用这个判断条件
      if (codeContainer && cbIndex+1 <= codeContainer.length) {
          return;
      }

      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);
      });
    });
  }
}());

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值