前言
自开始写博客主题以来,代码高亮的行号问题一直让我颇为头疼。记得初学前端时,我曾尝试过一些方法,比如利用代码行的固定高度来创建一个伪元素,并通过脚本生成大量行号内容,然后利用内容溢出剪切来实现行号。不过最近,我注意到一些博客主题开始增加了代码行号,这让我又燃起了研究这个功能的兴趣。
教程
首先,我们需要确保代码已经被highlight.js进行了高亮处理,这样可以避免在添加行号时产生解析冲突。
highlight.js的基本使用如下:
import hljs from 'highlight.js';
import javascript from 'highlight.js/lib/languages/javascript';
// 注册代码语言解析
hljs.registerLanguage('javascript', javascript);
// 对DOM元素进行代码高亮
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('pre code').forEach((el) => {
hljs.highlightElement(el);
});
});
接下来,我们观察了一些已经实现代码行号的网页的HTML结构,发现它们给每行代码都添加了一个带有自定义data属性的span标签,该属性的值即为行号。
基于这个发现,我们可以编写一个函数来添加行号:
function addLineNumber(codeDom) {
codeDom.classList.add("code-block-extension-code-show-num");
const codeHtml = codeDom.innerHTML;
const lines = codeHtml
.split("\n")
.map((line, index) => {
return `<span class="code-block-extension-code-line" data-line-num="${index + 1}">${line}</span>`;
})
.join("\n");
codeDom.innerHTML = lines;
}
这个函数首先获取到代码的HTML文本,然后通过split(“\n”)将其转换成数组。这样,数组中的每个元素就代表一行代码。接着,我们使用map函数遍历数组,并为每一行代码添加一个带有行号data属性的span标签。最后,我们使用join将数组重新转换成HTML文本,并更新DOM。
接下来,我们添加一些CSS样式来美化行号:
.code-block-extension-code-line {
background-color: inherit;
}
.code-block-extension-code-line::before {
content: attr(data-line-num);
color: gray;
user-select: none;
background-color: inherit;
text-align: center;
display: inline-block;
width: 18px;
padding: 0 10px;
box-sizing: content-box;
position: sticky;
left: 0;
}
这种方法基本上可以满足大部分代码展示的需求。不过需要注意的是,行号的宽度是固定的18px,如果你的代码行数非常多,可能需要调整这个宽度。
Typecho使用highlight.js代码高亮实现代码行号