因为已习惯用 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 官网:https://prismjs.com
- Prism Line Numbers 插件:https://prismjs.com/plugins/line-numbers
- Prism Normalize Whitespac 插件:https://prismjs.com/plugins/normalize-whitespace
评论 (0)