Hexo添加代码片段复制功能

作者 Leon Dong 日期 2023-12-07
Hexo添加代码片段复制功能

本文主要介绍如何给自己的Hexo博客内的代码片段添加“全部复制”的功能,这样可以很好的方便自己或者别人来对代码进行实验,并在此基础上改进。目前网上已经有较多的解决方案了,不过很多都是限定主题的(例如Next主题)。经过一番仔细搜索,我发现有些博主提供方案还是很通用的,在理解大致原理的基础上稍作修改就可以达成目标。

碎碎念

最近, 我把自己多年前写博客的习惯给捡回来了。一般来讲,在技术博客里面有点代码是很正常的事情,这不仅可以让别人更好的理解你的思路,也能让别人直接复制你的代码然后去实验和改进。不过对于一些比较长的代码片段,如果从头到尾用鼠标进行选中复制,还是比较麻烦且容易误操作的(选中的不完全或者太多等等)。因此,我觉得添加一个能直接复制整个代码块的按钮能大大提升这方便的使用体验。

本来以为这个事情挺复杂的(个人对前端开发经验较少),不过谷歌之后发现现有的解决方案还是很多的。我挑了一个比较通用的方法,在这位博主发表的博文的基础上进行理解并加以改进,直至最终完成。具体的步骤记录在以下几个小节当中。

最终代码复制的效果可以这篇博文进行参考。

详细步骤

为代码片段添加复制按钮

该部分可以说是整个功能的核心,大概的思路是利用JavaScript在页面加载完成后,在每个代码片段中添加一个复制按钮。在点击按钮时,会调用clipboard对象来完成对整个代码块的复制。

代码

详细的JavaScript代码如下。该代码基于原博文中提出的方法,稍作修改而来。值得一提的是,原博主认为他提出的方法适用于所有的Hexo主题,但事实上并非如此,他对代码快附近元素的结构是有一定假设的 (具体假设详见步骤3,一般情况)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
!function (e, t, a) {
var initCopyCode = function () {
var copyHtml = '';
copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
copyHtml += '<i class="fa fa-clipboard"></i>';
copyHtml += '</button>';
$("figure.highlight table").before(copyHtml);

var clipboard = new ClipboardJS('.btn-copy', {
target: function (trigger) {
var table = trigger.nextElementSibling.firstElementChild;
return table.firstElementChild.firstElementChild.firstElementChild.nextElementSibling;
}
});

clipboard.on('success', function (e) {
e.trigger.innerHTML = "<span>Done</span>"
setTimeout(function () {
e.trigger.innerHTML = "<i class='fa fa-clipboard'></i>"
}, 1000)

e.clearSelection();
});

clipboard.on('error', function (e) {
e.trigger.innerHTML = "<span>Failed</span>"
setTimeout(function () {
e.trigger.innerHTML = "<i class='fa fa-clipboard'></i>"
}, 1000)
e.clearSelection();
});
}
initCopyCode();
}(window, document);

我简单的对以上代码进行一下解析。

  1. 首先,我们创建一个HTML按钮元素 (3-6行)。

  2. 接下来,当页面加载完成时,我们将该按钮放置在figure.highlight 元素所包含的table元素之前(第7行)。如下图所示,箭头所示的位置就是我们想要插入的地方。值得一提的是,这种通过table元素而不是code本身的位置来添加按钮是原博主的方法,目的在于解决长代码下按钮位置不合理的问题。

  3. 定义剪贴板对象,需要传入的参数有你的按钮元素和target函数(9-14行)。其中target函数的实现需要你根据按钮元素(也就是传进来的trigger参数)来定位code元素并返回。也就是说,该函数需要根据你博客页面实际的结构来实现。

    • 一般情况:如上图所示,插入button按钮之后,页面元素的结构应该如下图所示。在这种情况下,我们可以利用代码trigger.nextElementSibling 来得到table元素,然后以table元素为基准,依次调用firstElementChild (tbody), firstElementChild (tr), firstElementChild (td.gutter), nextElementSibling (td.code), 来得到我们的目标元素.
    • 我的特殊情况:我的博客主题使用了响应式布局,且具体的实现方法是在代码块所在的table元素上嵌套一个table-responsive类型的div,而且这个行为是发生在按钮添加之后的。所以最终的页面布局就会像下图这样。这时候,上面那个定位table的代码就不再正确了。trigger.nextElementSibling 指向的是div元素,所以我们需要再加一个firstElementChild调用来找到正确的元素(见上述代码11行)。
  4. 后面的代码比较简单,就是定义复制成功或失败后的行为,简单来说就是在按钮的位置显示一下成功或者失败的消息,然后把按钮恢复原样。

使用方法

  1. 在Hexo博客根目录下的themes/[主题名称]/source/js 目录下创建一个新的JavaScript文件并填入上述代码内容(你可能需要更改target 函数中定位table的那一行代码),假设名字就叫做clipboard_use.js.

  2. 找到Hexo博客根目录下的themes/[主题名称]/layout/layout.ejs, 在body标签内部的最后加上如下代码

    1
    2
    3
    <script type="text/javascript" src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/clipboard@2.0.4/dist/clipboard.js"></script>
    <script type="text/javascript" src="/js/clipboard_use.js"></script>

    如果你的博客已经引入了jquery库,那第一行可以不需要。同理,上述代码中的jquery和clipboard源也可以自行更换。

为按钮定义CSS样式

这部分比较简单,主要就是利用CSS定义按钮的位置和样式。

代码

我对原作者的代码进行了一点点的的修改,取消了自动隐藏和鼠标移上时显示。 另外只保留的图标,去掉了文字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.btn-copy {
display: inline-block;
cursor: pointer;
background-color: #eee;
background-image: linear-gradient(#fcfcfc, #eee);
border: 1px solid #d5d5d5;
border-radius: 3px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-appearance: none;
font-size: 13px;
font-weight: 700;
line-height: 20px;
color: #333;
-webkit-transition: opacity .3s ease-in-out;
-o-transition: opacity .3s ease-in-out;
transition: opacity .3s ease-in-out;
padding: 2px 6px;
position: absolute;
right: 15px;
top: 7px;
opacity: 1;
}

.btn-copy span {
margin-left: 5px
}

使用方法

  1. themes/[主题名称]/source/css/style.css 中添加上述代码即可。如果目录里面没有style.css文件,那应该会有类似的其他文件。例如在我的主题中,该文件叫做blog_style.css

  2. 引入Font-Awesome CSS库。这个按钮的外形是通过这个库来定义的。具体方法是在themes/[主题名称]/layout/_partial/head.ejshead标签内加上如下代码。

    1
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.css">

    如果你的博客已经引入了该css库,那可以跳过这个步骤。