ブログにTOCを実装
目次(TableOfContent)というやつですね
要件
・各記事のh2項目を目次とする
・各記事の右側に表示する
・スクロールに追従する
・各項目はh2へのリンクになっている
よくあるタイプ
目次項目の抽出
Astroさんが大体用意してくれているので意外とあっさりできました
まずAstroのブログテンプレートで作成した状態の[..slug].astroで
const post = Astro.props;
const { Content, headings } = await post.render();
const toc = headings.filter((heading) => heading.depth === 2)
Contentを取得している所でheadingsも取得できます
今回はh2要素が欲しいのでdepth==2で
TOCを横に並べる
後述で作成したTOCを横に置きます
そのまま置くと下に置かれるのでflexに
<style>
.blog-post {
display: flex;
}
</style>
<BlogPost {...post.data}>
<div class="blog-post">
<div>
<Content />
</div>
<div>
<TOC headings={toc} />
</div>
</div>
</BlogPost>
TOCコンポーネント
今横に出てるもののコードは下記
これにheadingsとして前項目でフィルタしたものを渡してやるとOKな感じで
40行の簡素なもの
---
import type { MarkdownHeading } from 'astro';
interface Props {
headings: MarkdownHeading[];
}
const { headings } = Astro.props;
---
<style>
a {
text-decoration: none;
color: #333;
}
.toc {
position: sticky;
top: 0;
right: 0;
padding: 1rem;
background-color: #f8f8f8;
border: 1px solid #e1e1e1;
border-radius: 5px;
}
.item {
font-size: small;
margin-left: 1rem;
}
</style>
<ul class="toc">
<div>
{headings.map((heading) => (
<li class="item list-disc">
<a href={`#${heading.slug}`}><font size="3">{heading.text}</font></a>
</li>
))}
</div>
</ul>
ヘッダがある関係で少しおかしい挙動するけどまぁヨシ!