为博客添加代码块一键复制功能

 

参与开源第一步! 第一次的Pull Request, 献给TeXt。

前言

TeXt是目前个人网站采用的Jekyll主题, 简单易用, 风格非常符合我的口味; 很大程度激发了我的”创作”热情。由于博文中经常需要贴一些代码块, 一键复制代码是一个需求#200。本文记录此次的PR, zouyu4524:copy-to-clipboard

功能简述

  • 为博文中的代码块右上角添加复制到剪贴板按钮 , 点击即可复制代码块内容;
  • 类似Mathjax, 添加控制变量决定是否启用该功能;
  • 博文内各个代码块独立控制, 即: 可仅为特定的代码块添加。

bright dark

主要参考

clipboard.js是一款超轻量级的JS脚本, 实现复制到剪贴板功能。此次PR的代码依赖该脚本, 并且复制按钮的风格与操作逻辑也是相应参考其主页

实现逻辑

仿照TeXt中对Mathjax支持的实现, 相应修改实现以上提到的功能。首先引入控制变量clipboard指示是否需要开启该功能, 因为实现该功能需要加载clipboard.js脚本。在需要该功能的前提下, 主要包括两个环节:

  1. 添加相应的样式CSS;
  2. 查找页面中目标对象: pre且任意父级包含snippet class, 动态为其创建按钮(.btn)对象, 并且监听mouseenter1mouseleave2事件。

其中步骤1通过.scss实现, 样式同样来源于clipboard.js, 做了适当的精简, 保留了与TeXt项目中定义的样式类不冲突的部分; 而步骤2通过Javascript实现, 查找对象的核心代码如下:

var snippets = document.querySelectorAll('pre');
[].forEach.call(snippets, function(snippet) {
	if (snippet.closest('.snippet') !== null) {
		snippet.firstChild.insertAdjacentHTML('beforebegin', '<button class="btn" data-clipboard-snippet><img class="clippy" height="20" src="/assets/clippy.svg" alt="Copy to clipboard"></button>');
	}
});

其中document.querySelectorAll('pre')3负责查找代码块, 然后通过closest('.snippet')4判断找到的代码块任意父级是否为snippet class, 如此便可在博文中控制每个代码块是否提供一键复制的功能了。即: 如果按照原来的插入代码块的方式仍然不提供一键复制功能, 而如果需要提供该功能, 可以在代码块外套上一层snippet(目前的commit中已改为copyable), 如下:

<div class="snippet" markdown="1">

```
def hello():
    print('Hello world!')
```
{: .language-python}
</div>

集成到TeXt项目

上一节中介绍了实现此功能的两个步骤, 以下给出TeXt项目框架以及标注PR修改文件所在的路径。

TeXt项目结构

jekyll-TeXt-theme
 ├── _data
 |     ├── variable.yml
 |     └── ...
 ├── _includes
 |     ├── scripts
 |     |    ├── lib
 |     |    ├──  ├── copy-to-clipboard.js
 |     |    |    └── ...
 |     |    └── ...
 |     ├── clipboard.html // load external clipboard.js
 |     ├── copy-to-clipboard.html // determine whether to include clipboard.html and load copy-to-clipboard.js or not
 |     └── ...
 ├── _layouts
 |     ├── page.html
 |     └── ...
 ├── _sass
 |     ├── additional
 |     |    ├── _copy-to-clipboard.scss
 |     |    └── ...
 |     └── ...
 ├── assets
 |     ├── css
 |     |    └── main.scss
 |     └── ...
 ├── _config.yml
 └── ...

修改说明

  • variale.yml 添加了控制变量clipboard, 默认为false; 此外, 还添加了clipboard.js的CDN地址;
  • copy-to-clipboard.js 放置于_includes/scripts/lib下, 执行查找页面中目标对象并动态创建按钮的功能;
  • clipboard.htmlcopy-to-clipboard.html放置于_includes下, 分别用于加载外部clipboard.js和判断是否需要开启此功能;
  • page.html 添加了执行copy-to-clipboard.html的指令;
  • _copy-to-clipboard.scss5放置于_sass/additional下, 指定了与功能相关组件的样式; 相应在main.scss中引入此样式文件;
  • clippy.svg 放置于assets下, 脚本copy-to-clipboard.js中动态创建的按钮图片路径指向此文件;
  • _config.yml 中预设了clipboard变量为false

Tips

  • <script>默认是阻塞式的, 可以设置async选项改为非阻塞式;
  • JS代码需要注意执行顺序, 例如copy-to-clipboard.js依赖外部clipboard.js, 则需要先加载clipboard.js

总结

通过本次PR, 对TeXt项目的理解更进一步, 对Jekyll编译逻辑更加清晰, 对Liquid语法更熟悉。