clipboard.js 实现代码一键复制功能

  • 原创
  • Madman
  • /
  • /
  • 0
  • 1585 次阅读

clipboardjs-copy.png

Synopsis: 使用 Python-Markdown 将 markdown 原文转换为 HTML 内容后,借助于 clipboard.js(不依赖 Flash)将代码块复制到剪贴板

1. 思路整理

1.1 icon 固定到 div 的右上角

使用 Python-Markdown 将 markdown 原文转换为 HTML 内容后,大致格式如下:

<div>
    <div class="codehilite">
        <pre>
            codes...
        </pre>
    </div>
    ...
    <div class="codehilite">
        <pre>
            codes...
        </pre>
    </div>
    ...
    <div class="codehilite">
        <pre>
            codes...
        </pre>
    </div>
</div>

为了让 复制 图标 <a><i class="fa fa-clone"></i></a> 位于每个代码块 <div class="codehilite"> 的右上角,要使用 position 来定位,比如:

<div>
    <div class="codehilite" style="position: relative;">
        <a style="position: absolute; top: .3em; right: .5em;"><i class="fa fa-clone"></i></a>
        <pre>
            codes...
        </pre>
    </div>
    ...
    <div class="codehilite" style="position: relative;">
        <a style="position: absolute; top: .3em; right: .5em;"><i class="fa fa-clone"></i></a>
        <pre>
            codes...
        </pre>
    </div>
    ...
    <div class="codehilite">
        <a style="position: absolute; top: .3em; right: .5em;"><i class="fa fa-clone"></i></a>
        <pre>
            codes...
        </pre>
    </div>
</div>

其中 <a><i class="fa fa-clone"></i></a><pre>codes...</pre> 属于兄弟节点,它们都是 <div class="codehilite"> 的子节点

1.2 clipboard.js 介绍

官方文档: https://clipboardjs.com/

我们给上面的 HTML 中每个 <a> 标签添加 .copyicon,比如:

<a class="copyicon" style="position: absolute; top: .3em; right: .5em;"><i class="fa fa-clone"></i></a>

用于表示 clipboard.js 中的 trigger

当用户点击 复制 图标后,如何选择要复制的内容 target

2. 初步方案

假设使用 <div class="codehilite"> 表示 clipboard.js 中的 target 的话,由于 HTML 中有多个代码块,所以需要为每个代码块指定唯一的 id(id="code{}"),复制时通过 data-clipboard-target="#code{}" 选中对应的代码块

# 使用 Python-Markdown 将 markdown 原文转换为 HTML 内容(self.content_html),语法高亮是 markdown.extensions.codehilite 插件(依赖 pygments 模块)
# 为每个代码块添加 [复制] 按钮,然后前端页面再通过 clipboard.js 实现复制功能
n = self.content_html.count('<div class="codehilite">')
for i in range(n):
    self.content_html = re.sub(r'<div class="codehilite">',
                                '<div class="codehilite" style="position: relative;" id="code{}">'
                                '<a class="u-link-v5 g-color-main g-color-primary--hover copyicon" style="position: absolute; top: .3em; right: .5em;" data-clipboard-action="copy" data-clipboard-target="#code{}"><i class="fa fa-clone"></i></a>'.format(i, i), self.content_html, 1)

增加 JS 代码:

<script src="dist/clipboard.min.js"></script>
<script>
    var clipboard = new Clipboard('.copyicon');
    clipboard.on('success', function(e) {
        // console.log(e)
        Swal({
            toast: true,
            position: 'top',
            showConfirmButton: false,
            timer: 3000,
            type: 'success',
            title: '复制成功'
        });
        e.clearSelection();  // 清除文本的选中状态
    });
    clipboard.on('error', function(e) {
        // console.log(e);
        Swal({
            toast: true,
            position: 'top',
            showConfirmButton: false,
            timer: 3000,
            type: 'error',
            title: '复制失败'
        });
        e.clearSelection();  // 清除文本的选中状态
    });
</script>

clipboard01

存在问题: 复制的内容前面多出一个空行

因为复制了完整的 <div class="codehilite"> 导致包含 <a><i class="fa fa-clone"></i></a> 的缘故,它就是空行(如果 i 标签内部有文字,那么就不是空行,而是文字行)

2. 最终方案

官方文档提到可以设置 targettrigger 的下一个兄弟节点(<pre>codes...</pre>):

<script src="dist/clipboard.min.js"></script>
<script>
    var clipboard = new Clipboard('.copyicon', {
        target: function(trigger) {
            return trigger.nextElementSibling;
        }
    });
    clipboard.on('success', function(e) {
        // console.log(e)
        Swal({
            toast: true,
            position: 'top',
            showConfirmButton: false,
            timer: 3000,
            type: 'success',
            title: '复制成功'
        });
        e.clearSelection();  // 清除文本的选中状态
    });
    clipboard.on('error', function(e) {
        // console.log(e);
        Swal({
            toast: true,
            position: 'top',
            showConfirmButton: false,
            timer: 3000,
            type: 'error',
            title: '复制失败'
        });
        e.clearSelection();  // 清除文本的选中状态
    });
</script>

不再需要为每个代码块指定唯一 id,而且 trigger 也可以省去 data-clipboard-target 属性:

# 使用 Python-Markdown 将 markdown 原文转换为 HTML 内容(self.content_html),语法高亮是 markdown.extensions.codehilite 插件(依赖 pygments 模块)
# 为每个代码块添加 [复制] 按钮,然后前端页面再通过 clipboard.js 实现复制功能
self.content_html = re.sub(r'<div class="codehilite">',
                            '<div class="codehilite" style="position: relative;">'
                            '<a class="u-link-v5 g-color-main g-color-primary--hover copyicon" style="position: absolute; top: .3em; right: .5em;" data-clipboard-action="copy"><i class="fa fa-clone"></i></a>', self.content_html)

clipboard02

未经允许不得转载: LIFE & SHARE - 王颜公子 » clipboard.js 实现代码一键复制功能

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

0 条评论

暂时还没有评论.