JavaScript
2023.02.02(更新)
【CSSとJavaScriptのみ】ページ内リンクでスムーススクロールさせる方法
ページ内リンクをスムーススクロールさせたい!
今回はこんな要望に答えていきます。
本記事の内容
- プラグインを使わず、CSSとJavaScriptだけでページ内リンクをスムーススクロールさせる方法
本記事を対象とする方
- フロントエンドエンジニアを目指す方
- Webデザイナーを目指す方
この記事を書いている僕はプログラミング歴2年5ヶ月です。webエンジニアとして”社内転職”に成功しました。
CSSとJavaScriptでの設定方法
まずはページ内リンクとは何かから説明します!
ページ内リンクとは、同じページ内の特定部分にジャンプ(移動)するリンクのことです。
aタグのhref属性には”#1″、ジャンプ先の特定部分にはid=”1″というように設定します。つまり、href属性の#(シャープ)以降の文字列と、特定部分のidの文字列を完全一致させることでページ内リンクをつくることができます。コンテンツの多いWebページには必須の動作だと言えるでしょう。
設定するファイルはHTMLだけです。ただし、HTMLだけでは一瞬でジャンプするのみでスムーススクロールの挙動が起こりません。スムーススクロールさせるにはCSSもしくはJavaScriptで設定する必要があります。
実際にどうやるのか。CSSから説明していきます。と言ってもCSSは説明するほどではありません。なぜなら一行で終わるからです。htmlタグに、scroll-behavior: smooth;を設定するだけです。
- html {
- scroll-behavior: smooth;
- }
以上です。
あれ?あっさりしすぎじゃない?
なんだか簡単すぎて不安になるレベル…。
で、やはりその不安は的中。以下のような問題があり、あまりオススメできません。
- Safariに対応していない
- 全てのページ内リンクがスムーススクロールになる
- 細かい調整ができないためスクロール位置がズレる
- などなど
ですので、JavaScriptで設定するのが良いです。jQueryを使用する例はネットでたくさん検索ヒットするので、今回は生のJavaScriptのみを使用する場合で説明します。ちょっとだけ複雑になりますが、それでも難しいという内容ではありません。むしろ簡単な部類です。
ざっくりした内容は下記の通り。
- 目次(aタグ)をクリック
- ↓
- 該当する見出し(h2)の位置を取得
- ↓
- scrollTo()メソッドで取得した見出しの位置までページをスクロール
コードは以下のようになります。
- function scrollToHeading() {
- const a_all = document.querySelectorAll('a[href^="#"]');
- a_all.forEach((a,index) => {
- a.addEventListener('click', e => {
- e.preventDefault();
-
- const h2_all = document.querySelectorAll('h2');
- const h2_positionY = Math.floor(h2_all[index].getBoundingClientRect().top + window.scrollY);
-
- window.scrollTo({
- behavior:'smooth',
- top: h2_positionY,
- left: 0,
- });
- });
- });
- }
- window.addEventListener('DOMContentLoaded', () => {
- scrollToHeading();
- });
以下、解説です。
- const a_all = document.querySelectorAll('a[href^="#"]');
href属性が‘#’で始まる全部のa要素を取得。
- a_all.forEach((a,index) => {
- a.addEventListener('click', e => {
- e.preventDefault();
取得した全部のa要素にクリックイベントを設定し、取得した全部のa要素にデフォルトで設定されているイベントを全て無効化。
e.preventDefault();とは?
直訳すると、「デフォルトの動作を妨げる」となります。
つまり、preventDefault();を設定すれば、デフォルトの動作を無効化することができます。
ではデフォルトの動作とは何か?それはユーザー側の動作で発動するイベントのことです。
クリック動作、キーダウン動作、マウス動作などがわかりやすい例でしょう。
例えばaタグは、クリックするとhref属性に設定されたidにジャンプするデフォルト動作がありますが、それを無効化することができます。
今回では一瞬でジャンプするのではなく、スムースにスクロールさせたいので、そういった動作が邪魔なわけです。ですから最初に無効化させておく必要があります。
eというのはeventの略でして、その要素が持つイベント動作ということになります。
- const h2_all = document.querySelectorAll('h2');
- const h2_positionY = Math.floor(h2_all[index].getBoundingClientRect().top + window.scrollY);
リンク先となるh2全てを取得。h2は配列になっていて、h2_all[index]とすることで、aのインデックスとh2のインデックスを同期できます。indexはいつ取得したのかというと、foreachを使った際、2番目の引数で取得しました。foreachは3番目まで引数を指定できるので、覚えておきましょう。そうして取得した全てのh2のページトップからのY座標をh2_positionYに入れておきます。
Math.floorで小数点以下を切り捨て
getBoundingClientRect().top + window.scrollY);でh2のY座標を取得
- window.scrollTo({
- behavior:'smooth',
- top: h2_positionY,
- left: 0,
- });
scrollToメソッドを使い、h2_positionYの位置(h2の位置)までスクロールするように設定。スクロールの振る舞いはスムースにしておく(behavior: smooth;の部分)。
- window.addEventListener('DOMContentLoaded', () => {
- scrollToHeading();
- });
Webページには挿絵として画像リソースを入れる場合が多々あります。その際、そのリソースが読み込まれる前にブラウザがスクロールを始めると、ブラウザは画像の高さを計算せずにスクロールすることになるので位置がズレてしまいます。それを防ぐためにDOMContentLoadedで先にリソースが読み込んでおきます。デモページを作ったので、良かったら下記リンクからご覧ください。以上でJavaScriptでの設定は終了です。
スムーススクロールが効かないブラウザへの対応
スムーススクロールを設定したけど、あれ?効いてなくない?という方のために対応方法を解説します。
CSSのscroll-behaviorプロパティ、JavaScriptのscrollToメソッド。どちらも大変便利なのですが、ブラウザによってはサポートしていません。以下、サポート状況です。
Safariのサポート状況がイマイチなのが残念ですね。Safariがサポートされるまで待つ、というのも一つの手ですが、Polyfillを使えば解決できます。
Polyfillとは、最近の機能をサポートしていない古いブラウザーで、その機能を使えるようにするためのコードです。大抵はウェブ上の JavaScript です。
Polyfill (ポリフィル) – MDN Web Docs 用語集
色々なPolyfillがありますので、ご自身で調べてみてください。今回はこちらのPolyfillをご紹介します。
https://www.npmjs.com/package/scroll-behavior-polyfill
npmかyarnを使ってダウンロードします。npmやyarnの説明は今回は割愛しますね。
このPolyfillの開発者の説明ではダウンロード後、JSファイルにimportするだけで良いとのことなんですが、importしても正常に動かなかったので、index.jsを直接読み込みました。すると、Safariでも動くようになりました。けど、Chromeと比較すると、スクロールスピードが微妙に違う…。まぁ、そのくらいは良しとしますかね…。
今回のまとめ
今回はページ内リンクでスムーススクロールさせる方法について紹介しました。最後まで読んで頂きありがとうございました。
CSSでもJavaScriptでも実装できる
JavaScriptでの実装のほうがおすすめ
サポートしていないブラウザ(特にSafari)では、Polyfillを使う