LoginSignup
1
0

More than 3 years have passed since last update.

Gatsbyで超絶スムーズに動く目次のコンポーネント実装する

Posted at

こんな感じで、目次をクリックすると、超絶スムーズに目的地に到達する機能を実装します。

実際の動作は本家サイトgenkitech.netでご覧ください(まだ存在していたら)

やりたかったこと

  1. 好きな場所に配置できる目次コンポーネントの作成
  2. URLにpage#id のように#を追加せず、今のURLのままジャンプ(選択可)
  3. スムーズなスクロールUI

作り方

基本的なGatsbyの知識を前提とします。

必要なプラグイン

以下のプラグインを使用しているためインストールします。

  • gatsby-remark-autolink-headers(hタグにジャンプするためのIDを埋め込む)

  • gatsby-plugin-anchor-links(URLを変えずにジャンプするコンポーネントなど)

gatsbyの設定

gatsby-config.jsに以下の設定を追加します。

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} />

好きなところに目次ジャンプ機能を挿入できます。

image-20201120194331767.png

補足

好きなところにコンポーネントを配置できるようするため、親からgraphqlの情報を受け取らず、StaticQueryで全件取得して対象だけ使用しています。(gatsbyの画像でよくやる方法と同じです)

ただこの方法は、本番環境では影響がないものの、「gatsby develop」で開発してホットリロードされているときに、ブラウザでF5などで更新すると、develop開始時の情報に見た目上戻ってしまう事があったりします。

再びホットリロードさせるか、developコマンドを再実行すると最新情報で表示されるようです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0