ホームGatsby【連載】Gatsbyブログのデザインをワードプレスちっくにする手順(その6)Gatsbyブログの好きな位置に目次をつける方法。
2020年5月29日

【連載】Gatsbyブログのデザインをワードプレスちっくにする手順(その6)Gatsbyブログの好きな位置に目次をつける方法。

table

10月28日追記
Gatsbyブログについて、初noteを書きました。
【完全版】爆速GatsbyでWordPressちっくなブログを作る全手順

こんにちは、筋肉めがねです。

先日、親愛なる読者の方から、ブログに目次をつけると良いのでは、というアドバイスをいただきました。これまでの記事でも、「目次」のようなもの、を各記事のはじめの方につけてはいたのですが、この機会に、きちんとした「目次」を実装しました。

こちらが、目次です。

これまで使用していた目次は記事の中に文字を直打ちで書いておりましたが、今回実装した目次では、各ヘッダーの情報を自動で取得し、一覧にできるようになり、そして目次の各アイテムにリンクをつける事ができております。

また、このブログの記事はMarkdownファイルで書いているのですが、目次を記事の好きな箇所に表示することができるようになりました。

先ずは、“gatsby-remark-autolink-headers” pluginをインストールします。

このpluginは、Markdown記事内にヘッダーがある場合、それらのヘッダーにidを付与してくれるものです。

例えば、この章の見出しである「“gatsby-remark-autolink-headers” pluginのインストール」をChromeのdeveloper toolで確認すると、id=“gatsby-remark-autolink-headers pluginのインストール”が付与されている事を確認できます。

sample of header id

公式ページにあるとおり、npm installします。

Terminal
npm install --save gatsby-remark-autolink-headers

そして、gatsby-config.jsファイルに”gatsby-remark-autolink-headers” pluginを追加します。

gatsby-config.js
...
{
  resolve: 'gatsby-transformer-remark',
  options: {
    plugins: [
      ...
      {
        resolve: `gatsby-remark-autolink-headers`,
        options: {
          offsetY: `10`,
          icon: `<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>`,
          className: `custom-class`,
          maintainCase: false,
          removeAccents: true,
        },
      },
    ...

2020年6月26日追記
gatsby-remark-table-of-contentsを併せて使う場合(この記事の方法)は、gatsby-remark-autolink-headersのmaintainCaseはfalseにしてください。
gatsby-remark-table-of-contentsは、見出しをリンク先として目次に紐づけるんですが、その時に、見出しに入っているアルファベットを全て小文字にします。
そのため、見出しにidを付与するgatsby-remark-autolink-headersで、maintainCaseがtrueだと、idにアルファベットの大文字が含まれることになるからです。
そうなると、動作しません。

公式ページに書かれている通り、“gatsby-remark-prismjs” pluginを使用している場合には、そのpluginよりも前に”gatsby-remark-autolink-headers” pluginを追加します。

Optionsが幾つかありますが、特に大事なoptionは、offsetYとmaintainCaseです。 offsetYでは、目次のリンクをクリックした際に、リンク先のヘッダーが、スクリーン上部からどれくらい下の位置に表示されるか、設定できます。
maintainCaseは、ヘッダーの大文字、小文字を区別してidを付与してくれる、というものです。

“gatsby-remark-table-of-contents” pluginをインストール

続いて、“gatsby-remark-table-of-contents” pluginをインストールします。

こちらのpluginは、Markdownファイル内に目次(Table of Contents)を生成することができます。

公式ページにある通り、npm installします。

Terminal
npm i --save gatsby-remark-table-of-contents

そして、gatsby-config.jsに、このpluginを追加しましょう。

gatsby-config.js
...
{
  resolve: 'gatsby-transformer-remark',
  options: {
    plugins: [
      ...
      {
        resolve: `gatsby-remark-autolink-headers`,
        options: {
          offsetY: `10`,
          icon: `<svg aria-hidden="true" height="20" version="1.1" viewBox="0 0 16 16" width="20"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>`,
          className: `custom-class`,
          maintainCase: true,
          removeAccents: true,
        },
      },
      {
        resolve: `gatsby-remark-table-of-contents`,
        options: {
          exclude: "Table of Contents",
          tight: false,
          fromHeading: 1,
          toHeading: 6
        },
      },
    ...

これで、Markdownファイル内に目次を埋め込む準備は整いました。

Markdownファイルの好きな位置に「目次」を埋め込む

続いて、Markdownファイル内に目次を埋め込むため、以下の内容を、Markdownファイルの好きな位置に記述します。

```toc
# This code block gets replaced with the TOC
```

例えば、この記事の場合でしたら、以下のような形です。

先日、親愛なる読者の方から、ブログに目次をつけると良いのでは、
というアドバイスをいただきました。これまでの記事でも、
「目次」のようなもの、を各記事のはじめの方につけてはいたのですが、
この機会に、きちんとした「目次」を実装しました。

こちらが、目次です。

```toc
# This code block gets replaced with the TOC
```

そうすると、以下のように表示されます。

table of contents

もしも、gatsby-config.jsにて設定した内容を上書きしたい場合は、以下の通りに記述します。

```toc
# This code block gets replaced with the TOC
exclude: Table of Contents
tight: false,
from-heading: 1
to-heading: 6
```

これで、目次が記事の途中に表示されました。

「目次」テーブルを正しく表示するための各種設定

ここまでで、目次を記事の途中に表示することはできましたが、設定初期段階では、目次が思うように表示されない事があると思います。 そこで、このブログで起きた問題と解決策を1つ1つ書いていきます。

「目次」の前に見出しは使わない

目次の前に見出しを使うと、その見出し自体が目次の中の1つの項目としてリストされてしまうので、その問題を避けるために、このブログでは、目次の前には、見出しは使わないこととしております。

例えば、以下のような内容をMarkdownファイル内に書いていた場合は、##の代わりに>を使うなどしてます。

修正前

## 前回の内容
前回の記事では、、、、

そして、こちらが今回の目次です。
```toc
# This code block gets replaced with the TOC
```

修正後

> 前回の内容
前回の記事では、、、、

そして、こちらが今回の目次です。
```toc
# This code block gets replaced with the TOC
```

「目次」というタイトルを目次の上につける

目次の前に見出しは使いませんが、それでも、以下のような「目次」というタイトルはつけたいですよね。

下の画像の赤枠で囲んだ箇所です。

title of table of contents

このブログでは、scssにて、::beforeの擬似要素で対応しております。
“gatsby-remark-table-of-contents” pluginで、目次に付与されるclassは”toc”です。

.toc::before{
  content: " 目次";
  font-size: 1.375rem;
  color:#494949;
  border-left: solid 5px #7db4e6;
  padding-left: 10px;
  font-weight: 600;
  margin-left: .5rem;
}

目次以前にul要素があっても、目次の連番を必ず1から始める

目次よりも前に、ul要素があると、目次の連番が1以外の数字、つまり、前のul要素の連番を引き継ぐ事があります。

例えば、以下の画像のとおり。目次の連番が1から始まるはずが、2.1から始まってます。これは、scss内のcounter-incrementが正しく機能していないことが原因です。

bad example of numbering

これについては、以下の通り、.tocの中で付与する連番変数をitem2とし、他のul要素で使用しているitemと区別することで対応しております。

...
& ul {
  padding: 1.0em 0.5em 0.5em 0.5em;/*文字の上下 左右の余白*/
  background: #f4f4f4;/*背景色*/
  font-weight: 500;
  list-style-type: none;
  list-style-position:inside;
  counter-reset: item;

  & li{
    padding: 0em 1.7em;
    color: #00164B !important;
    word-break: break-all !important;
    &:before{
      counter-increment: item;
      content: counters(item, '.'); 
      display:inline-block;
      margin-right:15px;
      font-weight: 600;
      color: #5d93ff;
    }
    & a{
        color: #00164B !important;
        
        &:hover{
            color: #5d93ff !important;
        }
    }
    & p{
        display:inline;
    }
    & ul{
        margin-bottom: 0;
    }
  }
}
.toc{
  & ul {
    padding: 1.0em 0.5em 0.5em 0.5em;/*文字の上下 左右の余白*/
    background: #f4f4f4;/*背景色*/
    font-weight: 500;
    list-style-type: none;
    list-style-position:inside;
    counter-reset: item2;

    & li{
      padding: 0em 1.7em;
      color: #00164B !important;
      word-break: break-all !important;
      
      &:before{
          counter-increment: item2;
          content: counters(item2, '.'); 
          display:inline-block;
          margin-right:15px;
          font-weight: 600;
          color: #5d93ff;
      }
      & a{
          color: #00164B !important;
          
          &:hover{
              color: #5d93ff !important;
          }
      }
      & p{
          display:inline;
      }
      & ul{
          margin-bottom: 0;
      }
    }
  }
}
...

##の次は、必ず###、その次は####と、1つずつヘッダーの階層を下げる。でないと変なことになる。

このブログでは、基本的には、##を最上位の見出しとして使っております。 そして、見出しの階層は1つ、1つ下げていく必要がありますが、もしも、レベルを1つスキップして、例えば以下のようにMarkdownに記述すると、目次の中にブランクのアイテムが表示されることとなります。

### ##の次は、必ず###、その次は####と、1つずつヘッダーの階層を下げる。でないと変なことになる。
##### test

// ####を飛ばしている。

broken table of contents

こういう事にならないように、見出しのレベルは階層を1つ1つ下げながらmarkdownを記述しましょう。

“gatsby-remark-table-of-contents” pluginに頼らざるを得なかった背景

さて、ここまで目次の導入方法について書いてきましたが、最後に、他にもどういう手法を試したのかシェアしたいと思います。

このブログに目次をつける上で大事にしたかったことは、記事のどこでも好きなところに目次をつけれるようになる事でした。

それで、まず初めに、“post-template.jsx”の中で、GraphQLによりtableOfContentsを取り出し、それをPostTemplateDetails component内で表示させるという手法を試してみました。 そうすると、現状、そのComponentは以下のとおりになっているため、目次を、記事本文の前、もしくは本文直後に入れることはできても、本文中に入れることはできませんでした。

src/components/PostTemplateDetails/index.jsx
...
<div className="post-single__inner">
  <h1 className="post-single__title">{post.frontmatter.title}</h1>
  <div className="post-single__date">
    {moment(post.frontmatter.date).format('YYYY年M月DD日')}
  </div>
  <div className="post-single__body">{renderAst(htmlAst)}</div>
</div>
  <div className="post-single__footer">
  ...

そこで、今度は、以前Google Adsenseのcomponentをmarkdown内に入れ込んだ手法を使って、Table of Contents componentを作成し、markdownファイルへcomponentを入れようと試みましたが、StaticQueryで躓きました。

【更新版】Gatsby+NetlifyサイトへGoogle AdSenseを貼るベストプラクティス。(2020年2月8日時点)

Githubにて質問したStaticQueryに関するissue
How can I extract TableOfContents of each post and set this component in markdown file ?

最後に、component自体をmarkdownに埋め込む手法として、mdxフォーマットのmarkdownファイルを使う、という手法があります。

おそらく、mdからmdxへの移行ができれば、目的は実現できそうですが、いかんせんmdからmdxへの移行は、gatsby-node.jsを大幅に改修する必要があるため、リスクが高いと判断し、移行はしないこととしました。

How to convert an existing Gatsby blog to use MDX

という経緯があり、最終的に、“gatsby-remark-table-of-contents” pluginを使用することとしました。

まとめ

Gatsbyブログの好きな位置に目次をつける方法については以上です。

余談ですが、今回、Gatsbyブログに目次を導入する上で、沢山の情報をググりました。そして、2020年正月以降にGatsbyブログを立ち上げた、という方を沢山見つけました。Gatsbyでブログ作ってみよう、という方が増えてきているような印象です。そして昨日、GatsbyがシリーズBで$28Milの資金調達をしました。今後もさらなるGatsbyの飛躍に期待しましょう!

シェアする