因为已习惯用 Markdown 写文章,最近我将博客从 Wordpress 转到了Typecho。转换过程还是比较顺利的。我选的是 Joe 主题,最新版本 7.3.7。Joe 内置了代码高亮,无需借助任何插件。实际上是借助 Prism 来实现的,支持200种语言,但就是没有我要的显示代码行号功能。
纯 CSS 方案
记得以前做用的是纯 CSS 实现过加行号的功能,尝试能不能在这里管用。迅速找到以前的 demo 文件,代码很简单。
- HTML 片段:
<pre class="language-css"><code class="language-css">
<span class="token">code {</span>
<span class="token">counter-reset: step;</span>
<span class="token">counter-increment: step 0;</span>
<span class="token">}
<span class="token"></span>
<span class="token">code .token:before {</span>
<span class="token">position: absolute;</span>
<span class="token">left: 10px;</span>
<span class="token">content: counter(step);</span>
<span class="token">counter-increment: step;</span>
<span class="token">}</span>
</code>
</pre>
- CSS 片段:
code {
counter-reset: step;
counter-increment: step 0;
}
code .token:before {
position: absolute;
left: 10px;
content: counter(step);
counter-increment: step;
}
效果如下图:
这应该是我见过的最简单的显示代码行号实现了。正如我们所见,上述 CSS 样式我们声明了计数器 step,使用 counter-reset 属性重置计数器值为 0,使用 counter-increment 属性每行递增计数器值,然后在 content 属性上应用 counter() 函数将返回值显示在页面上。
注意:counter() 函数以字符串形式返回当前计数器的值。不支持 IE 浏览器。
我满怀希望的把这段 “魔法” CSS 代码片段应用到 Jeo 主题上。结果行号是可以显示出来,但行号重叠了😓。原来 demo 文件HTML 片段中每行只有一个 token,但是 Prism 渲染出来 token 一行可能有几个。难怪行号会重叠,所以这种方案是不可行的。如果我们还用 Prism 来高亮显示代码,就得借助它的 Line Numbers 插件了。
使用 Line Numbers 插件
让我们看另外一种方案 —— 使用Line Numbers 插件。插件安装起来简单。
- 导入依赖:
在 header 中引入 CSS 文件,然后在 body 最后一行的前面引入 JavaScript 文件。
<head>
<link href="https://prismjs.com/themes/prism.css">
<link href="https://prismjs.com/plugins/line-numbers/prism-line-numbers.css">
</head>
<body>
...
<script src="https://prismjs.com/prism.js"></script>
<script src="https://prismjs.com/plugins/line-numbers/prism-line-numbers.js"></script>
</body>
- 添加 line-numbers 样式类
在 <pre> 或其父容器上添加 line-numbers 样式类,如下所示:
<pre class="language-css line-numbers"><code class="language-css">
pre {
padding: 0 20px 0 40px;
}
code {
counter-reset: step;
counter-increment: step 0;
}
code .token:before {
position: absolute;
left: 0;
width: 40px;
text-align: right;
content: counter(step);
counter-increment: step;
}
</code>
</pre>
现在,在代码块的父标签或者代码块容器上面加入 line-numbers 类,就可以显示行号了。访问 codesandbox 查看实例效果。
不过不知你注意到没有,最终代码块的头部尾部多出两个行号。仔细看一下这个插件的使用文档是不能通过设置来解决这个问题的。时了一下,最简单的办法时将 <code> 开始标签放第一行,<code> 结束标签放最后一行。
如果代码块较多,调整起来未免麻烦,可以试试 Prism的
Normalize Whitespace插件。
解决整合问题
将 Line Numbers 插件整合到我的Joe主题后,我发现任没有行号,虽然代码块左边距比以前大。这就叫人尴尬了。是不是这个插件的bug 呢?得看看这个插件的源码。
Prism 需要 DOM 才能让大多数插件工作。查看 plugins/line-numbers/prism-line-numbers.js#L220 中的代码(还好源码就200 行左右),我们可以看到行号都被包裹在 <span aria-hidden="true" class="line-numbers-rows"> 中,每个行号对应一组空白内容的 <span></span> 元素标签。然后查看 plugins/line-numbers/prism-line-numbers.css 中的代码,我们发现这里得样式和先前demo 文件中得相似,每行 span 都包含一个伪选择器 ::before, 也是在 content 上应用 counter() 函数来显示返回的计数器值。不同的是,插件用 JavaScript 遍历创建出用来显示行号的 span 元素,每行一个元素,所以不会重叠。
回到不显示行号的问题,我用开发者工具仔细地研究了一下渲染出来的源码,发现插件其实已经生成行号 span 元素,伪选择器 ::before 下确实有相应的 CSS 样式属性。所以这个问题的根本原因在于 prism-line-numbers.css 样式不兼容 Jeo主题。略微调整一下样式,终于大功告成,完美解决了这个问题。可以看一下具体的代码改动:
pre[class*="language-"].line-numbers {
...
padding-left: 48px;
}
.line-numbers .line-numbers-rows {
...
left: 10px;
width: 30px;
...
}
30 + 10 + 8 = 48。好了,1000 行以内的代码显示行号时没有问题了。
提示:如果不得不兼容 IE,我们可以用 JavaScript 遍历
line-numbers-rows下所有的 span 元素, 用数组index + 1代替count()返回的函数值即可。
参考资源
- Prism 官网:prismjs.com
- Prism Line Numbers 插件:prismjs.com/plugins/lin…
- Prism Normalize Whitespac 插件:prismjs.com/plugins/nor…
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情