gatsby-plugin-anchor-links で数字始まりの id でも正常にページ内リンクさせる方法

2023/09/12に公開

GatsbyJS で作られたサイトで、ページ内リンクを使うために gatsby-plugin-anchor-links というプラグインを使用していますが、リンクさせたいターゲット要素の id が数字始まりのものだとエラーが出てしまい、ページ内リンクが動作しません。

この記事では、その原因と解決方法を説明します。

エラーの原因

以下のコードのように、数字始まりの idAnchorLink コンポーネントの to props に入れたものをクリックするとエラーが出て、ページ内リンクも動作しません。

import { AnchorLink } from 'gatsby-plugin-anchor-links'...
<AnchorLink to="#100-heading">ページ内リンク</AnchorLink>
<h2 id="100-heading">100 Heading</h2>...

このようなエラーが出ます。

Error in function module.exports in ./node_modules/scroll-to-element/index.js:30
Failed to execute 'querySelector' on 'Document': '#100-heading' is not a valid selector.

./node_modules/scroll-to-element/index.js:30

  28 | module.exports = function (elem, options) {
  29 |   options = options || {};
> 30 |   if (typeof elem === 'string') elem = document.querySelector(elem);
  31 |   if (elem) return scroll(0, calculateScrollOffset(elem, options.offset, options.align), options);
  32 | };
  33 |

このエラーを簡単に解説すると、gatsby-plugin-anchor-links は内部で scroll-to-element というライブラリを使っており、その中で document.querySelector() で ID を指定して、ターゲット要素を取得しています。

しかし、document.querySelector() は CSS セレクタの仕様を使います。CSS セレクタは idclass も先頭文字に数字は使えません。そのために SyntaxError となります。

この問題は gatsby-plugin-anchor-linksscroll-to-element のどちらにも Issue として上がっていますが、まだ対応されていない感じです。

scroll-to-element の方に PR も作られていますが、1年以上マージされず放置されているような状態なので、もう対応されることは期待できそうにもないですね。

解決方法

もちろん、数字始まりの id を使わないのが一番簡単な方法ですが、目次などの用途で見出しのテキストから動的に id を付与している場合などは、テキストを変えないといけなくなります。

ただ、案件によっては見出しのテキストを変えられない場合もあると思うので、その場合は何かしら別の方法で解決しないといけません。

要は、数字始まりの id の場合は、その数字をエスケープすれば CSS セレクタとして使えるので、以下のような関数を使って、先頭が数字の場合はエスケープするようにしました。

const escapeIdSelector = (id: string): string => {
  if (id.startsWith('#') && id[1].match(/\d/)) {
    return `#\\3${id[1]} ${id.slice(2)}`
  }
  return id
}

これを使って、先ほどのコードを以下のように書きます。

import { AnchorLink } from 'gatsby-plugin-anchor-links'...
<AnchorLink to={`${escapeIdSelector('#100-heading')}`}>ページ内リンク</AnchorLink>
<h2 id="100-heading">100 Heading</h2>...

これで、エラーも出ず、ページ内リンクも正常に動作するようになりました。

まとめ

gatsby-plugin-anchor-links で数字始まりの id でも正常にページ内リンクさせる方法について説明しました。

先ほども書いたように、動的に id を付与する場合でも、数字を先頭にしないようなロジックを書くなど、いろいろと解決方法はありますが、今回の私のケースの場合はこの「数字をエスケープする」方法が一番手っ取り早かったです。

もし、同じようなエラーで悩んでいる人がいましたら、参考にしていただけると嬉しいです。

Discussion