Hugo 生成文章的目录支持

因为一些长篇的博客文章准备作为浏览器导航页之一使用,需要快速定位链接,因此准备在博客模板中加入目录跳转功能。本文的方式主要参考Hugo添加文章目录toc

新建 TOC 模板

按照参考文章的内容,在 [theme]/layouts/patials/ 目录下建立 toc.html 这一目录模板,并在里面完善如下内容。

<!-- toc.html -->
<!-- ignore empty links with + -->
{{ $headers := findRE "<h[1-4].*?>(.|\n])+?</h[1-4]>" .Content }}
<!-- at least one header to link to -->
{{ if ge (len $headers) 1 }}
{{ $h1_n := len (findRE "(.|\n])+?" .Content) }}
{{ $re := (cond (eq $h1_n 0) "<h[2-4]" "<h[1-4]") }}
{{ $renum := (cond (eq $h1_n 0) "[2-4]" "[1-4]") }}

<!--Scrollspy-->
<div class="toc">

    <div class="page-header"><strong>- CATALOG -</strong></div>

    <div id="page-scrollspy" class="toc-nav">

        {{ range $headers }}
        {{ $header := . }}
            {{ range first 1 (findRE $re $header 1) }}
                {{ range findRE $renum . 1 }}
                {{ $next_heading := (cond (eq $h1_n 0) (sub (int .) 1 ) (int . ) ) }}
                    {{ range seq $next_heading }}
                    <ul class="nav">
                    {{end}}
                    {{ $anchorId := (replaceRE ".* id=\"(.*?)\".*" "$1" $header ) }}
                        <li class="nav-item">
                            <a class="nav-link text-left" href="#{{ $anchorId }}">
                            {{ $header | plainify | htmlUnescape }}
                            </a>
                        </li>
                    <!-- close list -->
                    {{ range seq $next_heading }}
                    </ul>
                    {{ end }}
                {{ end }}
            {{ end }}
        {{ end }}

    </div>

</div>
<!--Scrollspy-->

{{ end }}

引入 TOC 模板

由于本文使用的 noteworthy 模板在 single.html 中只定定义了用于博客文章内容显示的content div,如果直接在里面按照参考文章的方法在其中加入目录模板,点击跳转之后目录会跟着内容一同移动,而不能保持位置不变。因此我们需要另寻解决方案。经考虑,可以将 toc.html 加入 [theme]/layouts/_default/baseof.html 模板中,在其它页面中,因为不存在多级标题,不会显示目录,在内容页面其可以正常显示。修改后的 baseof.html 如下:

<!DOCTYPE html>
<!-- Default base template. Other templates define the "main" portion in this template -->
<html lang="{{ $.Site.LanguageCode | default "en" }}">
    {{ partial "head.html" . }}
    <body>
        {{ partial "nav.html" . }}
        <div id="content" class="content-container">
        {{ block "main" . }}
        {{ end }}
        {{ partial "math.html" . }}
        </div>

        <!-- toc的位置,它与nav、content两者形成页面的三栏结构 -->
        <div id="tableofcontent" class="toc">
        {{ if .Site.Params.toc | default false }}
        {{ partial "toc" . }}
        {{ end }}
        </div>
        {{ partial "footer-mobile.html" . }}
    </body>
</html>

样式调整

以上设置后,在 config.toml 中加入参数可以控制是否显示目录,即:

[params]
    toc = true

基本架构是可以的,但是相对位置不对,因此需要重新调整下css布局,在 [theme]/assets/css/partials/_breakpoints.scss 中修改 content class 并新增 toc 的样式设计,主要是调整两者的页面宽度。其他的样式就不需要了,能简则简。其他问题目前没有遇到。

    .content-container {
        // the changed setting
        max-width: 720px;
        margin-left: 310px;
        padding: 0 1.5em 0 0;
        height: 100%;
        display: flex;
        flex-direction: column;

        h1 {
            font-size: 1.9em;
            border-top: none;
            padding-top: 0em;
            margin-top: 1.4em;
        }
    }

    // new setting
    // TOC
    .toc {
        position: fixed;
        overflow: auto;
        height: 100%;
        top: 3em;
        left: 1100px;
        width: 280px;
        padding: 0px;
    }

参考文献

Hugo添加文章目录toc

Hugo Docs - Table of Contents