为博客文章生成标题列表

2022-11-10 ⏳1.7分钟(0.7千字)

我写过一些较为系统性介绍某些主题的文章,主要面向初学者。因为要讲求内容的完整性和前后的逻辑性,文章内容不免写得很长。但是内容越长,对初学者的劝退作用也就越强。最近需要给组内同学编写Go语言入门材料,需要趁此机会把这个问题解决掉,方便大家阅读。

最简单的办法就是将文章内容划分成不同的部分,每一部分设置标题,然后生成一个标题列表。我的博客系统是自研的,一直都没有实现该功能。

我的博客使用 pandoc 将 Markdown 转换成 HTML。而 pandoc 原生支持生成标题列表。我们需要在生成 Markdown 的时候指定--toc参数:

pandoc -s --toc a.md -o a.html

toc表示table-of-content。pandoc 会在正文前面自动生成如下列表:

<nav id="TOC" role="doc-toc">
<ul>
  <li><a href="#id1">t1</a></li>
  <li><a href="#id2">t2</a></li>
  <li>
    <a href="#id3">t3</a>
    <ul>
      <li><a href="#id4">t31</a></li>
      <li><a href="#id5">t32</a></li>
    </ul>
  </li>
</ul>
</nav>

我给自己的博客加上--toc参数后并没有看到新生成的<nav>标签。我在网上查了半天,有说需要在 Markdown 文件的 Front Matter 中指定toc变量:

---
title: "xxx"
toc: true
---

试了一下还是不行。 仔细对比 pandoc 命令的参数,最主要的区别是我的博客使用了自定义的 HTML 模版。我怀疑 pandoc 生成标题列表需要在模版中指定特殊的变量。但从 pandoc 官网看了半天也只找到了$toc$这一个变量。

实在没办法了,我搜索pandoc html default template直接找到 pandoc 默认的 HTML 模版文件,跟标题列表相关的部分如下:

$if(toc)$
<nav id="$idprefix$TOC" role="doc-toc">
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$
$table-of-contents$
</nav>
$endif$

简单来说就是通过$toc$变量的取值决定是否显示标题列表。可以使用变量$toc-title$为列表指定单独的标题。然后列表内容需要使用$table-of-contents$变量指定。所以,只需要把上面的模版代码复制到我自己的模版,就可以自动生成列表。

开启--toc参数后,pandoc 默认会为所有包含h1-h6标签的文章生成标题列表。但有时候虽然文章里用到了标题,但并不想生成标题列表。就比如我的关于页面。这种情况需要在 Front Matter 中指定toc:false就可以了。

pandoc 生成的列表跟 Water.css 配合,展示效果还算可以。本来我还想自己重新订制样式,搞一个绝对定位。但想想太麻烦,先不折腾了。用20%的成本解决80%的问题也就可以了😄