Front End Programming
  1. ホーム
  2. ブログ一覧
  3. Gatsbyブログサイト移行物語4~プラグインを利用して目次出力~

Gatsbyブログサイト移行物語4~プラグインを利用して目次出力~

JavaScriptReactGatsby

記事に目次をつけたかったのでプラグインgatsby-remark-autolink-headersを利用して目次を実装しました。

ulタグからolタグに変え、目次が長くなるので閉じるボタンをつけ、アコーディオンさせるなど少し改造しました。そのやり方について綴ります。

この記事を書いた人

かみーゆ/フロントエンドエンジニア

セブ島在住の気ままなフリーランスエンジニア。テクニカルディレクター・エンジニア講師・ブリッジSEを経て今に至る。CMS concrete5エバンジェリスト。テックブログ以外も「磨耗しない人生の選択」や「海外生活」のライフスタイルについて発信。好きなものは肉とビール。

Read More

今までのGatsbyの記事と注意点

現在ここまで記載しています。
制作するまでを目標にUPしていくので順を追ったらGatsbyサイトが作れると思います。

  1. インストールからNetlifyデプロイまで
  2. 投稿ページの充実と画像の方法
  3. ブログ記事、カテゴリー、タグ一覧の出力
  4. プラグインを利用して目次出力(←イマココ)
  5. プラグインナシで一覧にページネーション実装
  6. 個別ページテンプレート作成
  7. プラグインHelmetでSEO調整
  8. CSSコンポーネントでオリジナルページを作ろう!!
  9. 関連記事一覧出力

このシリーズではテーマGatsby Starter Blogを改造

この記事は一番メジャーなテンプレート、「Gatsby Starter Blog」を改造しています。同じテーマでないと動かない可能性があります。

GatsbyJSは豊富なプラグインが魅力です。

gatsby-remark-autolink-headersはプラグインの1つです。
以下のようなことができます。

  • 見出しタグにidを振る
  • 見出しタグを抽出しリンク付きのリストタグを出力

gatsby-remark-autolink-headers

npmコマンドで手軽にインストールできます。

npm install gatsby-remark-autolink-headers

gatsby-config.jsにプラグインの追記

gatsby-remark-autolink-headersはgatsby-transformer-remarkのサブプラグインです。
なので、gatsby-config.jsのgatsby-transformer-remarkのoptionに記載します。

テーマGatsby Starter Blogを利用していればgatsby-transformer-remarkはインストールされているはずです。

module.exports = {
  plugins: [
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [`gatsby-remark-autolink-headers`],
      },
    },
  ],
}



このプラグイン1コ問題があって、公式サイトによるとプラグインgatsby-remark-prismjsよりも前に読み込む必要があります

// good
{
  resolve: `gatsby-transformer-remark`,
  options: {
    plugins: [
      `gatsby-remark-autolink-headers`,
      `gatsby-remark-prismjs`,
    ],
  },
}

// bad
{
  resolve: `gatsby-transformer-remark`,
  options: {
    plugins: [
      `gatsby-remark-prismjs`, // should be placed after `gatsby-remark-autolink-headers`
      `gatsby-remark-autolink-headers`,
    ],
  },
}

今回はシンプルにアイコンなし。以下のように設定しました。

オプションの説明については記事の後ろに記載します。

module.exports = {
  plugins: [
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-autolink-headers`,
            options: {
              icon: false,
              maintainCase: true,
            },
          },
        ],
      },
    }
  ]
}

目次を出力するコンポーネントを作成する

次に目次を出力するコンポーネントを作成します。

src/components/内にtopics.jsを追加します。

src/
    ├ templates/
    |   └ blog-post.js
    └ components/
        └ topics.js(新規作成)

コードはこんな感じです。

import React from "react";

const Topic = props => {
  return (
    <div class="mokujiList">
      <h2>目次</h2>
        <div
          dangerouslySetInnerHTML={{
            __html: props.data,
          }}
        >
        </div>
      </div>
  );
};

export default Topic;

リスト化されたデータはdata.markdownRemark.tableOfContentsに格納されます。

blog-post.jsのクエリのmarkdownRemark()内にtableOfContents必ず追記してください。

あとは記事の読み込みたい場所にコンポーネントを出力するだけです。

import Topic from "../components/topic"

//~コード省略~

const BlogPostTemplate = ({ data, location }) => {
  {/*読み込みたい場所に挿入*/}
  <Topic data={data.markdownRemark.tableOfContents} />

  //~コード省略~

}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug(
    $id: String!
    $previousPostId: String
    $nextPostId: String
    $hero: String
  )
  {
    site {
      siteMetadata {
        title
      }
    }
    allFile(
      filter: {
        relativePath: {eq: $hero}
        sourceInstanceName: {eq: "assets"}
      }
    ){
      edges {
        node {
          name
          relativePath
          childImageSharp {
          fluid(maxWidth: 800) {
          ...GatsbyImageSharpFluid_withWebp
        }
            }
          }
        }
      }
    markdownRemark(
      id: {eq: $id }
    ) {
        id
        excerpt(pruneLength: 160)
        html
        tableOfContents
        frontmatter {
          title
          date(formatString: "YYYY.MM.DD")
          description
          lead
          hero
          category
          cateId
          tags
          pagetype
          modifieddate(formatString: "YYYY.MM.DD")
        }
      }
      previous: markdownRemark(id: {eq: $previousPostId }) {
        fields {
          slug
        }
        frontmatter {
          title
        }
      }
      next: markdownRemark(id: {eq: $nextPostId }) {
        fields {
          slug
        }
        frontmatter {
          title
        }
      }
    }
  }
`

出力されるタグをulからolに変え、開閉ボタンをつける

リスト出力がul(アンオーダーリスト)なのは個人的にはちょっと気に入らないです。
なのでここから少し改変します。

このサイトではolはすでにカウンター関数を利用してスタイリングしてあります。

CSSカウンターを使ってリストタグにナンバーを振る
CSSカウンターを使ってリストタグにナンバーを振る

1-2、2-3みたいにリストや見出しにナンバーが振られているサイトを見か・・・

JSのreplaceul>からol>に置換します。
(閉じタグもあるのでこのような形にしました)

後ほどアコーディオン機能を実装します。チェックボックスを追加してh2labelに書き換えておきます。

import React from "react";

const Topic = props => {
  const list = props.data.replace(/(ul>)/gi, 'ol>');

  return (
    <div class="p-box--gray u-mblg">
      <input type="checkbox" class="mokuji" id="mokuji" />
      <label class="c-content__heading" for="mokuji">目次</label>
      <div class="c-editArea mokujiList">
        <div
          dangerouslySetInnerHTML={{
            __html: list,
          }}
        >
        </div>
      </div>
    </div>
  );
};

export default Topic;

アコーディオンはCSSで実装する

アコーディオンは手間なのでCSSのみで実装しました。

今回はコードしか紹介しませんので詳しく原理を知りたい方はこちらをご覧ください。

セブ島唯一日本人占い師・さくら庵のマイアさんから学ぶかみーゆ流占いとの付き合い方
CSS3 アニメーションで軽量なアコーディオンメニューを作ろう!

CSS3 アニメーションって便利ですよね?jQueryに依存しないと軽量・・・

HTMLはこんな感じで出力されます。コードが長いので省略しています。

<div class="p-box--gray u-mblg">
  <input type="checkbox" class="mokuji" id="mokuji">
  <label class="c-content__heading" for="mokuji">目次</label>
  <div class="c-editArea mokujiList">
    <div>
      <ol>
    ここリストが出力されます
      </ol>
    </div>
  </div>
</div>



CSSです。モジュール化してもいいですが私はCSSに直書きしました。
CSSモジュール化の仕方についてはポートフォリのページを作った際に実装したので改めて記事化しますね。

.mokuji {
  display: none;
}

.mokuji:checked ~ .mokujiList {
  max-height: 0;
}

.mokuji ~ .mokujiList {
  max-height: 200vh;
  transition: .3s;
  overflow: hidden;
}

.mokuji + .c-content__heading {
  position: relative;
  display: block;
}

.mokuji + .c-content__heading:before {
  transition: .3s;
  position: absolute;
  content: "";
  width: 30px;
  height: 1px;
  top: 10px;
  right: 0px;
  background: #464675;
  display: block;
}

.mokuji:checked + .c-content__heading:after {
  transform: rotate(0deg);
}

.mokuji+ .c-content__heading:after {
  transform: rotate(90deg);
  transition: .3s;
  position: absolute;
  content: "";
  width: 30px;
  height: 1px;
  top: 10px;
  right: 0px;
  background: #464675;
  display: block;
}



最初から閉じておきたい場合は、inputcheckedを付与しておきましょう。

<input type="checkbox" class="mokuji" id="mokuji" checked>

オプションの一覧

オプションの一覧です。icon以外はあまり使うことないかもしれません。

オプション 用途
offsetY リンクをクリックして移動した時の見出しの上の空き(オフセット)の調整。pxです
icon Boolean。デフォルトはtrueでホバーすると左にアイコンが表示されまます。
class アンカーに独自のクラス名を指定するそう
maintainCase Boolean。含まれる要は大文字小文字を維持するか指定できる。
removeAccents Boolean。アクセント削除。日本人のサイトにはまず必要なさそう。
isIconAfterHeader Boolean。アイコンの位置を右側に移動
elements リンクを自動挿入するためのタグ一覧を配列で指定

まとめ

目次があるとこの記事はどんなコンテンツが含まれてるか読者にわかりやすいのでオススメです。

サイト改修ついでにGatsbyのことを記事化してますが、このサイトがやっとサイトとして機能するようになり私もホッとしました。

この記事が皆さんのコーディングライフの一助となれば幸いです。

最後までお読みいただきありがとうございました。

関連記事もあわせてお読みください

Web制作に関する人気の記事

海外移住・ライフスタイルに関する人気の記事

キャリアアップ・転職に関する人気の記事