技术类的博客,不来点代码高亮总觉得逼格不够。下面就说说如何加入这个功能吧,所有主题通用(其他类型网页也可以举一反三,方法都是类似的)。
准备
按照效率来说,使用PHP的方案自然是最好的。但鉴于使用PHP会造成需要修改的地方比较多比较麻烦,所以就不考虑了。这次就用一个比较常见JavaScript库来解决——highlight.js。这个库简单易用,而且自带很多种样式选择,总有一种适合你。
选择样式
这里有很多种预设样式可以选择。左边的Language categories就是选择语言的分类,Styles就是选择样式,右边就可以查看效果。确定好要使用的样式之后,到这里找到对应的css文件,点开后把代码复制下来保存到主题目录就行了。比如我就保存到主题下的css/highlight.css
文件中。
然后就要把样式引入主题中,最简单的方法就是修改主题的头部文件header.php
(视主题而定,一般是这个文件)的</head>
前添加这样一行:
<link rel="stylesheet" href="<?php echo esc_url( get_template_directory_uri() ); ?>/css/highlight.css">
<?php echo esc_url( get_template_directory_uri() ); ?>
是输出当前主题的网络路径。可以根据需要修改。
关于CDN
highlight.js我们可以通过网上的CDN来引入,也可以放到自己的网站上。比如使用百度的CDN,就上百度静态资源公共库搜索hightlight,然后就会得到结果了。如果放到自己的网站上,你就得考虑相对路径、绝对路径等等,这里不做讨论(有问题请留言)。下面会以path/to/highlight.js
来统一指代。
题外话:关于HTTPS和HTTP的引用
有的网站既有HTTP访问又有HTTPS访问,这时网页中的资源引用就会出现问题。如果从HTTPS页面引用HTTP的资源就会被浏览器拒绝。
这时可以使用一种无协议的引用方式,当然前提是被引用资源要两种都能访问。比如:
<script src="//example.com/code.js"></script>
这样在HTTP页面上,浏览器就会引用http://example.com/code.js
,在HTTPS页面上就会引用https://example.com/code.js
。对于CSS也可以使用这种方式引用。
最简单的方法:让highlight自动处理
这种方法是让highlight自动处理相关的代码块,只要在主题脚部文件footer.php
(视主题而定,一般是这个文件)的</body>
前加入如下代码:
<script src="path/to/highlight.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
第一行是引用highlight.js的文件,第二行是执行初始化。
highlight.js会自动寻找结构为<pre><code>...</code></pre>
的代码块,并自动判断语言。也可以通过<code>
元素的类来指定语言或者不要高亮。支持的语言名称和别名可以在这里查到(右边那一列就是)。语言前也可以加上language-
或者lang-
。不要高亮则设置为nohighlight
。比如:
<pre><code class="php">PHP代码</code></pre>
<pre><code class="lang-html">HTML代码</code></pre>
<pre><code class="language-js">JavaScript代码</code></pre>
<pro><code class="nohighlight">不要高亮</code></pre>
如果装过之前介绍的Markdown插件的,还可以更方便。可以用三个或更多`
来代表代码块,并在后面加上直接加上类名。比如:
```php
PHP代码
```
```language-html
HTML代码
```
```lang-js
JS代码
```
```nohighlight
不要高亮
```
高级方法:无法保证代码块的结构时,或是代码块动态生成时
上面的方法要求代码块的结构是<pre><code>...</code></pre>
这样的,如果不能保证这样的结构我们就需要额外的处理了。另外,如果代码块是动态加载的,也需要额外处理。
加载JS的代码是一样的,只不过不用初始化了:
<script src="path/to/highlight.js"></script>
我们要把元素传递给highlight.js的hljs.highlightBlock
接口处理,这个接口接受一个参数,就是需要处理的DOM元素。和上面类似,我们可以用类属性来指定语言种类。
如果代码不是在pre
或者设置过CSS属性white-space
保留换行的元素之中的话我们需要用<br>
元素来换行。这种情况我们要先设置一下让highlight使用<br>
来换行(以下为JavaScript代码,需要放到<script>
元素中,或者放到外部文件中通过<script>
元素的src
属性引用):
hljs.configure({useBR: true});
然后我们就要选择所有的包含代码的元素然后传递给highlight.js处理。比如我们的代码块结构是<div><code>...</code></div>
这样的,那么就要用如下代码:
code_elms = document.querySelectorAll('div code');
// code_elms = Array.prototype.slice.call(code_elms);
for(var i=0; i <= code_elms.length; i++) {
code_elm = code_elms[i];
hljs.highlightBlock(code_elm);
}
document.querySelectorAll
是受到主流浏览器的支持的通过类CSS选择器来选择元素的接口,和jQuery的选择器很类似,返回的是一个类数组对象。但实际上并不是数组,其中的成员会随着DOM的变化而变化,所以如果页面上的元素会动态发生变化,最好取消第二行的注释以将其转换为真正的数组。
如果对ECMAScript6感兴趣,那么这个还可以更简化
Array.from( document.querySelectorAll('div code') ).forEach( code => hljs.highlightBlock(code) );
这里用到了ES6的Array.from
来转换为数组元素,然后用forEach
方法来遍历每个成员。然后用箭头函数来定义了forEach
的回调函数。鉴于浏览器支持的原因,不建议这样使用。
还可以用jQuery,这个在highlight.js的官网上就给出例子了:
$('div.code').each(function(i, block) {
hljs.highlightBlock(block);
});
高级方法:使用Web Workers
什么是Web Workers,简单点说就是在后台执行JavaScript代码的。使用Web Worker可以避免在执行耗时代码时当前页面假死。highlight.js在处理量非常大的代码时有可能会假死,所以放到Web Workers中执行是很好的解决方法。
使用Web Workers就不需要在页面中引用highlight.js,我们将在Worker中引用。
首先我们需要修改header.php
文件,在</head>
之前加入:
<script>
addEventListener('load', function() {
var codes = document.querySelectorAll('pre code');
if(codes.length > 0) {
for(var i=0; i<codes.length;i++) {
var code=codes[i];
var msg = {
lang: Array.prototype.slice.call(code.classList),
content: code.textContent
};
var worker = new Worker('<?php echo esc_url( get_template_directory_uri() ) ?>/js/highlight-worker.js');
worker.onmessage = function(c) {
return function(event) { c.innerHTML = event.data; }
}(code);
worker.postMessage(msg);
}
}
});
</script>
第10行中因为我们引用了外部文件,所以用<?php echo esc_url( get_template_directory_uri() ) ?>
输出了主题目录的网络路径。这段代码的意思就是,在窗口的load
事件上绑定了行为:选择所有符合pre code
选择器的元素,然后遍历每一个元素,把元素的内容和类属性通过msg
对象传递给Worker,然后把Worker返回的数据替换元素的内容。Worker是使用一个叫做highlight-worker.js
的文件建立的。
根据上面的代码可以看出,highlight-worker.js
我放到了主题目录下的js/highlight-worker.js
,其内容如下:
onmessage = function(event) {
importScripts('path/to/highlight.js');
var result = event.data.lang.length==0 ? self.hljs.highlightAuto(event.data.content) : self.hljs.highlightAuto(event.data.content, event.data.lang);
postMessage(result.value);
}
这段代码是在Worker的message
事件上绑定行为:先导入highlight.js文件,然后根据传入的数据(也就是前面的msg
对象)的lang
属性的有无,调用hljs.highlightAuto
接口来处理传入的数据的content
属性(也就是代码内容)。然后把处理结果返回。
这样就行了。
最后
如果想查看highlight.js的完整API列表,可以到这里。还可以研究一下自己修改样式。虽然这个方案并不是最佳的,比如效率问题,比如高亮还是不如一些代码编辑器,比如不会加行号等等,但对于代码的可读性已经是很不错的提升了,更复杂的功能以后再研究吧。
打赏微信支付宝
没有评论