- 本文地址: https://www.yangdx.com/2020/04/144.html
- 转载请注明出处
前端代码高亮插件 highlight.js,默认是不带行号显示功能的。如果要显示行号,我们得自己实现,或是使用第三方插件。下面就来介绍几种实现方式,并分析优缺点。
1、第一种,使用 <li></li> 标签实现行号
当时还在写这个博客程序时,考察了行号显示功能,发现网上一些文章采用这样的方式自己实现行号显示:以换行符 \n
分割,将每行代码用 <li></li>
标签进行包裹,再在最外层用 <ol></ol>
标签包裹,设置 css 样式,利用 ol
列表内的 li
自动编号的特性,轻松的实现了行号显示。
这个方法最简单,但是有缺陷:遇到多行文本高亮着色时,highlight.js 是在第一行前面加 <span class="xxx">
标签,在最后一行末尾加 </span>
闭合。如果简单的用 <li></li>
对每行进行包裹,就会造成 <span>
标签没有正常闭合,又根据 html 标签自动闭合的特性,仅有第一行能正常着色!
添加行号前:
添加行号后:
因为这个缺陷,所以我放弃使用这个方法。
2、第二种,使用 highlightjs-line-numbers.js 插件显示行号
highlightjs-line-numbers.js 这个插件应该是用得比较多的了,目前在 github 上有 243 个 star,本博客程序最初也是使用这个插件。
他的实现方式是,将 highlight.js 处理过的每行代码用 <td></td>
标签包裹,且每行前面又加一个新的 <td></td>
作为行号,结构就是 <tr><td>{行号}</td><td>{代码}</td></tr>
,最后在最外层使用 <table></table>
包裹。也就是,行号在表格的第一列,代码在表格的第二列。行号是通过 css 设置的 :before{content:attr(data-line-number)}
,且设置了不可选中 user-select: none
。
此插件的作者考虑到了多行文本的 <span>
标签闭合问题,所以不会出现像第一种方法那样的缺陷。这也是当初我为什么选择这个插件的原因。
但是,在上周,我仔细观察时发现,这个插件在处理空行的时候,把空行替换成了空格。肉眼观察,当然不会有什么问题,只有复制粘贴的时候,才会发现多出来的这个空格。可是有些时候,我们的空行是有特殊意义的,插件最好不要对我的代码进行任何改动!
基于这个原因,我决定放弃 highlightjs-line-numbers.js 插件,用自己的办法实现行号显示。
3、第三种,我自己的实现方式
我的思路是,在每一行代码的行首加上 <span class="line-num" data-num="xxx"></span>
标签,然后通过 css 设置 :before 伪元素样式 content:attr(data-num)
显示行号,并对伪元素的位置设置偏移,增加行号和代码的间距。
由于我增加的这个标签是在每行的行首,所以它一定是在最左边(自动对齐),且不会破坏前面所讲 <span class="hljs-string"></span>
多行文本标签的闭合。又由于我增加的标签不包含新的文本内容,所以并未改动原代码。
下面就是我自己写的行号插件代码(参考或直接引用了 highlightjs-line-numbers.js 的部分代码)。
javascript 代码:
(function (w, d) {
w.hljsln = {
initLineNumbersOnLoad: initLineNumbersOnLoad,
addLineNumbersForCode: addLineNumbersForCode
};
function initLineNumbersOnLoad() {
if (d.readyState === 'interactive' || d.readyState === 'complete') {
documentReady();
} else {
w.addEventListener('DOMContentLoaded', function () {
documentReady();
});
}
}
function addLineNumbersForCode(html) {
var num = 1;
if (/\r|\n$/.test(html)) {
html += '<span class="ln-eof"></span>';
}
html = html.replace(/\r\n|\r|\n/g, function (a) {
num++;
var text = (' ' + num).substr(-3);
return a + '<span class="ln-num" data-num="' + text + '"></span>';
});
html = '<span class="ln-num" data-num=" 1"></span>' + html;
html = '<span class="ln-bg"></span>' + html;
return html;
}
function documentReady() {
var elements = d.querySelectorAll('pre code');
for (var i = 0; i < elements.length; i++) {
if (elements[i].className.indexOf('hljsln') == -1) {
var html = elements[i].innerHTML;
html = addLineNumbersForCode(html);
elements[i].innerHTML = html;
elements[i].className += ' hljsln';
}
}
}
}(window, document));
css 代码:
pre {
position: relative;
}
.hljsln {
display: block;
margin-left: 2.4em;
padding-left: 0.7em !important;
}
.hljsln::-webkit-scrollbar {
height: 15px;
}
.hljsln::-webkit-scrollbar-thumb {
background: #666;
}
.hljsln::-webkit-scrollbar-thumb:hover {
background: #797979;
}
.hljsln::-webkit-scrollbar-thumb:active {
background: #949494;
}
.hljsln .ln-bg {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 2.4em;
height: 100%;
background: #333;
}
.hljsln .ln-num {
position: absolute;
z-index: 2;
left: 0;
width: 2.4em;
height: 1em;
text-align: center;
display: inline-block;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.hljsln .ln-num::before {
color: #777;
font-style: normal;
font-weight: normal;
content: attr(data-num);
}
.hljsln .ln-eof {
display: inline-block;
}
将 js 代码保存为文件 highlight.line-numbers.js,加载并执行初始化如下:
<!-- 引入代码高亮插件 highlight.js -->
<script src="highlight.pack.js"></script>
<!-- 引入代码行号显示插件 highlight.line-numbers.js -->
<script src="highlight.line-numbers.js"></script>
<script>
// 初始化代码高亮插件
hljs.initHighlightingOnLoad();
// 初始化代码行号显示插件
hljsln.initLineNumbersOnLoad();
</script>
跟 highlightjs-line-numbers.js 对比,我的 js 代码少了很多,像 css 隔行变色、:hover 当前行变色等效果暂无法实现。
下面是添加行号后的代码效果如下:
插件已提交至码云:https://gitee.com/yangrz/highlight.line-numbers.js
如果您有更好的建议,欢迎留言!
非常感谢,很有用
成功解决前来感谢
博主回复:
能帮到你我很荣幸,欢迎交流!