実現したいこと
マークダウンで記述した記事の冒頭に、目次を自動的に表示したい。
記事の見出しにはリンクを自動的に生成して、目次からその見出しへ飛べるようにする。
2 カラムレイアウトでは、サイドバーにも目次を自動的に表示する。サイドバーの目次はスティッキーにする。記事の閲覧箇所に関係なく 目次が常に表示 されようにして、更なる閲覧性の向上を図りたい。
前提条件
既に React-Bootstrap が適用済みで、2 カラムレイアウトが作成済である事を前提とする。React-Bootstrap の適用方法は こちらの記事 で、2 カラムレイアウトの作成方法は こちらの記事 で説明している。Gatsby Starter Blog を利用しているが、他のスタータでも大きな違いはないはず。
実装
基礎
最も大切なことは gatsby-transformer-remark が、GraphQL で HTML 形式の目次を提供 していること。GraphQL で tableOfContents(absolute: false, maxDepth: 3)
などと記述すると、dangerouslySetInnerHTML
で描画可能な目次が得られる。
もう一つは、gatsby-remark-autolink-headers プラグインを導入すると、マークダウン記事の見出しにホーバーリンクを自動生成してくれること。
この2つを組み合わせれば、目次の表示と、目次から見出しへ飛ぶ機能が簡単に実現できる。素晴らしい!!
Toc コンポーネントの作成
GraphQL で取得した目次をプロパティで受け取って表示する Toc コンポーネント (src/components/Toc.js) を作成する。
const Toc = ({ toc }) => {
// falsy: '', null, ...
if (!toc) return '';
return (
<div className={`mb-4 p-3 bg-light ${styles.toc}`}>
<h2>目次</h2>
<div dangerouslySetInnerHTML={{ __html: toc }} itemProp="articleBody" />
</div>
);
};
Toc コンポーネントの配置
マークダウン記事の冒頭に目次が表示されるように、 src/templates/blog-post.js に Toc コンポーネントを挿入する。
<Layout snip... >
<Seo snip... />
<article itemScope itemType="http://schema.org/Article">
<header className="mb-4">
snip...
</header>
<Toc toc={post.tableOfContents} />
<section
className={styles.blogPost}
dangerouslySetInnerHTML={{ __html: post.html }}
itemProp="articleBody"
/>
<hr />
</article>
snip...
</Layout>
ちなみに、記事の任意の場所に目次が配置できる gatsby-remark-table-of-contents もあるが、今回は使っていない。
次にサイドバーにも Toc コンポーネントを配置する。Layout コンポーネント (src/components/Layout.js) の、Tags コンポーネント(タグ一覧)の後ろに配置する。目次をスティッキーにするために、Bootstrap の sticky-top
クラスを指定してる事に注目!
const Layout = ({ children, toc }) => {
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
}
}
}
`);
const siteTitle = data.site.siteMetadata?.title || `Title`;
return (
<>
<NavBar siteTitle={siteTitle} />
<Container>
<Row>
<Col lg={8}>
<main>{children}</main>
</Col>
<Col lg={4}>
<Bio />
<Tags />
<div className="sticky-top">
<Toc toc={toc} />
</div>
</Col>
</Row>
<footer className="py-4">
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.com">Gatsby</a>
</footer>
</Container>
</>
);
};
注意事項
gatsby-remark-autolink-headers の設定 (gatsby-config.js)
- gatsby-remark-prismjs と共存する場合は、gatsby-remark-autolink-headers を gatsby-remark-prismjs の前(上)に配置する
- gatsby-remark-table-of-contents で目次を表示するときは、
maintainCase: false
の指定が必要らしい (未検証)
gatsby-transformer-remark が生成する目次のスタイリング
箇条書きの 一段目だけ li
の中に p
が含まれてて、目次が 部分的に行間が空いて 間抜けに表示される。これ僕だけ?
そこで Toc コンポーネントのスタイル (src/components/Toc.module.css) で、p { margin-bottom: 0rem; }
と指定して修正する。
参考リンク
- Gatsby.js ブログへの目次追加 | うしじのブログ ← gatsby-transformer-remark & gatsby-remark-autolink-headers
- GatsbyJS サイトに目次(table of contents)を付ける | Vanilla note ← スティッキーな目次コンポーネント
- Gatsby.js の Markdown 記事に目次を生成する ← gatsby-remark-table-of-contents
- 【連載】Gatsby ブログのデザインをワードプレスちっくにする手順(その 6)Gatsby ブログの好きな位置に目次をつける方法。 - 筋肉めがね ← 目次のスタイリングまで解説が詳しい