スムーススクロールをJavaScriptのみで実装してみる

2022/01/10
thumbnail

タイトルの通り、スムーススクロールをJavaScriptだけで実装してみました。スムーススクロールというのは、ページ内リンクを押したときになめらかに移動する仕様のことです。

jQueryを使用していませんが、コードもシンプルかつ軽量なのでおすすめです。

はじめに:実装にあたって

以前から目次のリンクをクリックしたとき、見出しに瞬間移動するのがなんとなく味気なく感じていました。そんなときにスムーススクロールの存在を知り、これはいいなぁと。でもjQueryはできれば使いたくない…

そこで、JavaScriptのみでの実装を決意。ついでにヘッダーと見出しがかぶる問題もどうにかしたいなぁと思っていたので、両方を解決するスクリプトを作成しました。いろいろと試行錯誤した結果、満足できる仕上がりになったと思います。

ちなみに、最近オリジナルの自動生成目次も作りました。ブログサービスを問わず使えますので、こちらのほうもぜひチェックしてみてください↓

【全見出しタグ対応】高速&シンプルな自動生成目次 | IB-Note

いろいろな自動生成目次のソースコードを読んで比較検討を重ねた結果、「これだ!」と思う理想の目次を作ることができました。

スクリプトの特徴と使い方

スクリプトの特徴は以下の通りです。

  • #が付いたaタグのリンクからIDを取得。
  • リンクをクリックしたとき、対象要素になめらかに移動。
  • ヘッダー高さ分のオフセットを指定できる。

以下がスクリプトです。</body>の直前に設置します。

<script>
(function(){
  const offset = 70; // ヘッダーの高さ
  const links = document.querySelectorAll('a[href^="#"]');
  links.forEach((link) => {
    link.addEventListener('click', (event) => {
      event.preventDefault();
      let href = link.getAttribute('href').substr(1);
      href = href == '#' ? 'html' : `[id="${href}"]`;
      const target = document.querySelector(href);
      const rect = target.getBoundingClientRect();
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const position = rect.top + scrollTop - offset;
      window.scrollTo({
        top: position,
        behavior: 'smooth'
      });
    });
  });
}());
</script>

目次の見出しに#toc-1のようにIDが付いている場合、上記スクリプトを設置するだけでスムーススクロールの機能が有効になります。

#から始まるリンクのaタグをすべて対象にしているので、例えば<a href="#comments"> ... </a>のような場合もスムーススクロールの効果が付与され、リンク先までなめらかに移動するようになります。

コード中にコメントを入れていますが、offsetでヘッダーの高さ分のオフセット値を調整できます(デフォルトは70px)。ヘッダーを最上部に固定表示などしていない場合は、0に設定していただければOKです。

コードの解説

以下の部分では、「aタグのうちhrefが#から始まるもの」をすべて取得しています。

const links = document.querySelectorAll('a[href^="#"]');

querySelectorAllと正規表現的な表現を組み合わせることで、jQueryに近い柔軟な指定をすることができるので便利ですね。

要素を取得すると、それぞれの要素(リンク)に対してクリックイベントを登録します。リンクはそのままだとクリックした瞬間に遷移してしまうので、以下のコードでデフォルトの挙動を禁止します(最初ここでハマって2時間ほど費やしました)。

event.preventDefault();

取得したリンク要素をもとに、移動先要素の情報を取得していきます。以下のコードでは、リンク要素のhrefから移動先要素のIDを取得します。

let href = link.getAttribute('href').substr(1);
href = href == '#' ? 'html' : `[id="${href}"]`;

hrefが#のみの場合はページ全体(html)を指定し、それ以外は'[id="XX"]'となるように指定します。

hrefから移動先要素を取得すると、移動先要素の絶対位置(position)を計算し、以下のコードで望みの位置までなめらかにスクロールします。

window.scrollTo({
  top: position,
  behavior: 'smooth'
});
コメントはまだありません