Gatsbyで超絶スムーズに動く目次のコンポーネント実装する
こんな感じで、目次をクリックすると、超絶スムーズに目的地に到達する機能を実装します。
やりたかったこと
- 好きな場所に配置できる目次コンポーネントの作成
- URLにpage#id のように#を追加せず、今のURLのままジャンプ(選択可)
- スムーズなスクロールUI
作り方
基本的なGatsbyの知識を前提とします。
必要なプラグイン
以下のプラグインを使用しているためインストールします。
-
gatsby-remark-autolink-headers(hタグにジャンプするためのIDを埋め込む)
-
gatsby-plugin-anchor-links(URLを変えずにジャンプするコンポーネントなど)
gatsbyの設定
gatsby-config.jsに以下の設定を追加します。
...
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
...
{
resolve: `gatsby-remark-autolink-headers`,
options: {
icon: false, //Hタグ横のアイコンを消す場合false
},
},
...
]
}
},
{
resolve: "gatsby-plugin-anchor-links",
options: {
offset: -100 //スクロール先のオフセット
}
},
...
コンポーネントを作成する
以上でHタグにIDが埋め込まれ、<AnchorLink>
コンポーネントを配置するとジャンプするようになったので、以下の目次表示コンポーネントを作成して画面に追加します。
下の方の sx={{}} の所は「theme-ui」の機能を使ってスタイリングしているだけなので、上部のimportと一緒にとりあえず外してしまっても大丈夫です。
/** @jsx jsx */
import { jsx } from "theme-ui"
import React from 'react';
import { graphql, StaticQuery } from 'gatsby';
import { AnchorLink } from "gatsby-plugin-anchor-links";
export const TableOfContents: React.FC<{
className?: string,
slug: string,
}> = props => (
<StaticQuery
query={graphql`
{
allMarkdownRemark {
edges {
node {
headings {
depth
id
value
}
fields {
slug
}
}
}
}
}
`}
render={(data: any) => {
//graphqlでは全件取得しているため対象を特定
const headings = data.allMarkdownRemark.edges.find((n:any)=>{
return n.node.fields.slug == props.slug })?.node?.headings;
return (headings && <div sx={{display: 'grid',gap: 2}}>
{headings.map((x:any)=><AnchorLink
className={props.className??'' + ' toc-link toc-depth-'+x.depth}
//末尾にスラッシュを付ける場合はこっち
to={"/" + props.slug + '#' + x.id}
//末尾にスラッシュを付けない場合はこっち
//to={"/" + props.slug.replace('/','') + '#' + x.id}
stripHash //#表示を追加しない
sx={{ //theme-uiでスタイルを設定
display: 'block',
ml: ((Number(x.depth)-2)*15)+'px',
textDecoration: 'none',
fontSize:'0.95rem',
lineHeight: '1.1rem',
'&:hover': { textDecoration: 'underline' }
}}
>
{x.value}
</AnchorLink>
)}
</div>);
}}
/>
);
使い方と解説
上の<TableOfContents>
コンポーネントはslugをキーにして対象のドキュメントを検索するため、post.jsなどの記事を表示しているコンポーネントで以下のようにすると
<TableOfContents slug={post.fields.slug} />
好きなところに目次ジャンプ機能を挿入できます
補足
好きなところにコンポーネントを配置できるようするため、親からgraphqlの情報を受け取らず、StaticQueryで全件取得して対象だけ使用しています。(gatsbyの画像でよくやる方法と同じです)
ただこの方法は、本番環境では影響がないものの、「gatsby develop」で開発してホットリロードされているときに、ブラウザでF5などで更新すると、develop開始時の情報に見た目上戻ってしまう事があったりします。
再びホットリロードさせるか、developコマンドを再実行すると最新情報で表示されるようです。