【Hugo】ブログの記事ページのサイドバーに固定目次を追加

 

こちらの記事で、ブログにサイドバーを追加しました。

今回はこのサイドバーに目次を追加していきます。


完成イメージ

機能的には、

  • クリックしたらリンクしてその項目に飛ぶ
  • スクロールしてもついてきてくれる
  • 必要ない時には隠れる

ぐらいが欲しい。

デザインは、既にあるサイドバーの項目(プロフィール)のものを踏襲する形にします。

完成イメージ


記事ページの目次

Hugo 本ブログで使用している静的サイトジェネレータ
公式サイト
では、 h タグから自動的に目次を生成してくれる機能が備わっているので、

とりあえずそれを利用してみます。

sidebar.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<div class="sidebar">
    <div class="profile">
        <h4 class="sidebar-header">プロフィール</h4>
        ...
    </div>
    {{- if .IsPage -}}
        {{ $enableTOC := .Params.toc | default .Site.Params.enableTOC -}}
        {{- if $enableTOC -}}
            <div class="sidebar-toc">
                <h4 class="sidebar-header">目次</h4>
                {{- .TableOfContents -}}
            </div>
        {{- end -}}
    {{- end -}}
</div>

記事以外のページには目次は必要ないということで、

記事ページにのみ表示という意味で6行目。

enableTOC は config.toml やマークダウンファイルの フロントマター マークダウンファイルの先頭の、その記事に関する情報を書く部分 で指定されるもので、

目次を表示するかしないかの設定です。

11行目が Hugo の目次を呼び出しています。

これで一応サイドバーに目次を設置することはできました。

「クリックしたらリンクしてその項目に飛ぶ」も最初からついています。

ちなみに Hugo の作った目次の構造は…

ちなみに Hugo の作った目次の構造は次のようになっています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<nav id="TableOfContents">
    <ul>
        <li>
            <a href="#見出し1">見出し1</a>
            <ul>
                <li>
                    <a href="#見出し2">見出し2</a>
                </li>
                <li>
                    <a href="#見出し3">見出し3</a>
                </li>
            </ul>
        </li>
        <li>
            <a href="#見出し4">見出し4</a>
        </li>
    </ul>
</nav>

このとき目次欄には

  • 見出し1
    • 見出し2
    • 見出し3
  • 見出し4

のように表示されます。


あとの2つの要件「スクロールしてもついてきてくれる」「必要ない時には隠れる」は

CSS と JavaScript で対処します。

スクロールしてもついてきてくれる

CSS でposition: fixed;とすれば、スクロールしても動かなくなります。

その後、topleftで位置を調整します。

custom.scss
1
2
3
4
.sidebar-toc {
    position: fixed;
    top: 0;
}

しかし、これではスクロール量が少ないときに、ヘッダやサイドバーの他の項目と重なってしまいます。

必要ない時には隠れる

スクロール量を取得して、一定の値より小さければ非表示にします。

custom.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// sidebar-toc というクラス名の要素のリストを取得し、その最初の要素を取得
var toc = document.getElementsByClassName('sidebar-toc')[0];
 
if (toc) {
    // スクロールが起きたときに関数を実行
    window.addEventListener('scroll', function () {
        // スクロール量が一定値より大きいとき
        // sidebar-toc のクラスに show を追加
        window.scrollY > 1000 ? toc.classList.add('show') : toc.classList.remove('show');
    });
}

クラスを追加することによって、これを CSS で表示・非表示を切り替えることができるようになりました。

custom.scss
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.sidebar-toc {
    position: fixed;
    top: 0;
    opacity: 0;             // 透過度を0にして非表示
    pointer-events: none;   // リンクを無効に
    transition: opacity 0.5s ease-in-out;
    &.show {
        opacity: 1;             // 透過度を1にして表示
        pointer-events: auto;   // リンクを有効に
    }
}

display: none;だとアニメーションできず瞬間で消えたり現れたりするので、

フェードイン・フェードアウトするようにopacityを使っています。

pointer-eventsは、マウスポインタをあてたときに反応する領域を指定するもので、

noneに設定することでリンクを無効にできます。

関連記事

【Hugo】ブログにマウスオーバーでふわっと表示する吹き出しを追加

【Hugo】ブログにマウスオーバーでふわっと表示する吹き出しを追加


以上で記事ページに目次を設置することができました。

しかし、これと同じ方法では.TableOfContentsが使えない場合に目次を設置することはできません。

長くなってしまったので、そのへんのお話はこちらをどうぞ。

関連記事

【Hugo】.TableOfContents が使えないけど目次を追加したいときの対処法

【Hugo】.TableOfContents が使えないけど目次を追加したいときの対処法

最後まで読んでくださってありがとうございました👋