第一篇: hexo主题迁移and折腾记

第二篇:hexo主题模版改造记录

本来在第二篇的时间就打算把剪贴板这件事搞定的,但是一拖拖了好久。

起因

要知道很多 Hexo 模版都提供剪贴板功能,以及备案号展示功能,但是我这模版太早期了,要是用最新版本的那么自己的代码部分又要全部重新弄,我是非常不愿意这么做。那就不如自己来实现了。然后搜了很久以后我看到这么一篇文章:在 Hexo 主題內新增程式碼片段複製功能 这就很好啊,很符合我的要求。于是动工。

思路

如果直接修改模版的话似乎也不是不行,但是那样的话可能会极大降低灵活度,另外我在翻看页面文件的时候也并未发现指定代码框的部分,因此还是直接用 JS 操作 DOM Tree 吧。

代码框部分

如图我们基本可以看到实际上应该一个 highlight 下面俩子节点一个 code 一个 gutter ,这样的话其实如果按照上面链接内文章操作的话是在这段的顶部套一个 DIV 然后插入按钮什么的进去,但是这样似乎对我的这个模板来说不是很友好,所以最后我会选择插入这么个玩意儿:

1
2
3
4
5
6
7
    <div id="codeblock-titlebar">
        <span id="lang-title" style="font-size: 5px"></span>
        <button class="codecopy-btn tooltipped tooltipped-sw" aria-label="Copy to clipboard">
            <span class="fa fa-copy" aria-hidden="true"></span>
            click to copy
        </button>
    </div>

这样就好多了,也不需要担心顶层按钮后面放不了东西背景空一块出来。

操作

但是其实只要观察过就会知道,整个代码框的部分全部是动态的,也就是说我们无法根据元素进行定位,只能使用 hexo 给定的父级对象操作,然后针对每个对象都如此操作才能在每个代码框中都插入按钮。因此代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    var snippets = document.querySelectorAll('figure.highlight');
    var htmlCopyButton = `
    <div id="codeblock-titlebar">
        <span id="lang-title" style="font-size: 5px"></span>
        <button class="codecopy-btn tooltipped tooltipped-sw" aria-label="Copy to clipboard">
            <span class="fa fa-copy" aria-hidden="true"></span>
            click to copy
        </button>
    </div>
    `;

    snippets.forEach(snippet => {
        var parent = snippet.parentNode;
        var wrapper = document.createElement('div');
        parent.replaceChild(wrapper, snippet);
        wrapper.appendChild(snippet);
        wrapper.classList.add('code-highlight');
        wrapper.firstChild.insertAdjacentHTML('beforebegin', htmlCopyButton);
        var lang = (snippet.classList[1] || 'code').toUpperCase();
        wrapper.setAttribute('data-lang', lang);
    });

虽然我们无法直接操作,但是把我们需要的部分包裹起来就好办很多。

复制按钮定位

根据 clipboard.js 的文档,直接调用后复制的内容是所调用的标签下的内容,但是我们的按钮的话直接调用会不起任何作用的,还是得找个合适的节点,我们可以在查看一下 td标签的那一行:

我们需要的部分是:

1
<td class"code">...</td>

上面一堆操作以后目前的结构是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div class="code-highlight">
    <div id="codeblock-titlebar">
    	<span>...</span>
        <button>...</button>
    </div>
    <figure class="highlight lang"><!--实际lang会被替换为编程语言的名称-->
    	<table>
            <tbody>
                <tr>
                	<td class="gutter">...</td>
                    <td class="code">...</td>
                </tr>
            </tbody>
        </table>
    </figure>
</div>

这就其实比较尴尬,大概顺序是我们的按钮的父级的下一个节点的子节点的子节点的子节点的第二个节点才是我们需要的内容(看着就绕),但是实际上确实没有好的处理办法(处理的越简洁速度越慢,详情请见廖雪峰的博客)。所以我们还是老老实实的写吧:

1
2
3
4
5
6
7
var clipboard = new ClipboardJS('.codecopy-btn', {
        target: function(trigger) {
            var copytext = trigger.nextSibling.parentElement.nextElementSibling.querySelector('.code');
            return copytext;
            // console.log(copytext);
        }
    });

最后倒是投机取巧的直接找 .code 解决掉,不过速度也不差,就那样吧。关于 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
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//@import "fontawsome-all.min.css";

// ref: https://github.com/primer/primer/blob/master/modules/primer-tooltips/lib/tooltips.scss
.tooltipped {
  position: relative;
}

// This is the tooltip bubble
.tooltipped::after {
  position: absolute;
  z-index: 1000000;
  display: none;
  padding: 0.5em 0.75em;
  -webkit-font-smoothing: subpixel-antialiased;
  color: #fff;
  text-align: center;
  text-decoration: none;
  text-shadow: none;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: break-word;
  white-space: pre;
  pointer-events: none;
  content: attr(aria-label);
  background: #616161;
  border-radius: 3px;
  opacity: 0;
}

// This is the tooltip arrow
.tooltipped::before {
  position: absolute;
  z-index: 1000001;
  display: none;
  width: 0;
  height: 0;
  color: #616161;
  pointer-events: none;
  content: '';
  border: 6px solid transparent;
  opacity: 0;
}

// delay animation for tooltip
@keyframes tooltip-appear {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

// This will indicate when we'll activate the tooltip
.tooltipped:hover,
.tooltipped:active,
.tooltipped:focus {
  &::before,
  &::after {
    display: inline-block;
    text-decoration: none;
    animation-name: tooltip-appear;
    animation-duration: 0.1s;
    animation-fill-mode: forwards;
    animation-timing-function: ease-in;
  }
}

// Tooltipped south
.tooltipped-s,
.tooltipped-sw {
  &::after {
    top: 100%;
    right: 50%;
    margin-top: 6px;
  }

  &::before {
    top: auto;
    right: 50%;
    bottom: -7px;
    margin-right: -6px;
    border-bottom-color: #616161;
  }
}

.tooltipped-sw::after {
  margin-right: -16px;
}

// Move the tooltip body to the center of the object.
.tooltipped-s::after {
  transform: translateX(50%);
}

.codecopy-btn {
  border: none;
  display: inline-block;
  margin: 0 0;
  z-index: 999;
  float: right;
  background-color: #efefef;
  box-shadow: none;
  outline: none;
  height: 100%;
}
.codecopy-btn:hover{
  background-color: #b6b6b6;
  text-decoration-color: #1b1f23;
}

@media print,
screen and (max-width: 900px) {
  .codecopy-btn {
    font-size: 2vh;
  }
}

@media print,
screen and (max-width: 850px) {
  .codecopy-btn {
    font-size: 1vh;
  }
}

.code-hightlight{
  position: absolute;
  box-sizing: border-box;
  z-index: -5;
}

#codeblock-titlebar {
  margin: 0 0;
  padding: 0 0;
  width: 100%;
  height: 4vh;
  background-color: #efefef;
  position: relative;
  z-index: 9999;
  display: block;
  float: bottom;
}

因为上面 titlebar 的作用是填补空白部分因此也就不怎么讲究了直接拉满即可。

(未完)