sp_menu

【JavaScript】スムーススクロールを実装する方法

【JavaScript】スムーススクロールを実装

今回は、JavaScriptでスムーススクロールを実装する方法をご紹介します。

この記事は以下のお悩みを解決します。

  • ・スムーススクロールの実装方法がわからない
  • ・CSSプロパティ「scroll-behavior」を使わずにスムーススクロールを実装したい

1. スムーススクロールとは

スムーススクロールはリンクをクリックするとページ内を目的の要素まで滑らかに移動するアニメーションのことです。

CSSのプロパティ「scroll-behavior」でスムーススクロールを実装できますが、safariがバージョンが15.4以降にしか対応していないのでブラウザに幅広く対応するという意味ではまだJavaScriptで実装したほうが適切かもしれません。

2. スムーススクロールの実装

See the Pen smooth scroll by Hiro (@Coffee1610) on CodePen.

2-1. HTMLのコード解説

<header>
 <nav>
  <ul class="nav-menu">
   <li class="nav-item"><a href="#section1">section1</a></li>
   <li class="nav-item"><a href="#section2">section2</a></li>
   <li class="nav-item"><a href="#section3">section3</a></li>
   <li class="nav-item"><a href="#section4">section4</a></li>
   <li class="nav-item"><a href="#section5">section5</a></li>
  </ul>
 </nav>
</header>

<div class="container">
  <section id="section1">
   <p>section1</p>
  </section>
  
  <section id="section2">
   <p>section2</p>
  </section>

  <section id="section3">
     <p>section3</p>
  </section>

  <section id="section4">
   <p>section4</p>
  </section>

  <section id="section5">
   <p>section5</p>
  </section>
</div>

1-9行目:<header>~</header>

headerタグの中にsection1~5のリンクを記述しています。href属性は頭に#(ハッシュ)、その後ろに目標の要素のid名を続けて記述します。

これによりリンクと指定したセクションが結びつきます。

2-2. CSSのコード解説

*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

a {
  text-decoration: none;
}

.nav-menu {
  list-style: none;
}

header {
  background: #02153f;
  padding: 0 10px;
  position: fixed;
  width: 100%;
  height: 84px;
}

nav {
  height: 100%;
}

.nav-menu {
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: 100%;
}

.nav-item:not(:last-child) {
  margin-right: 15px;
}

.nav-item a {
  color: #FFF;
}

.container {
  padding-top: 84px;  
}

section {
  height: 250px;
  display: grid;
  place-items: center;
}

#section1 {
  background: #ff6060;
}

#section2 {
  background: #b2fd87;
}

#section3 {
  background: #62d5e4;
}

#section4 {
  background: #9e97ff;
}

#section5 {
  background: #ff96ed;
}

36行目:padding-top:84px;

headerがposition:fixedになっているため、containerクラスのdivがheaderの下に潜り込んでしまいます。containerクラスのdivが隠れてしまわないようにpadding-topでheaderの高さ分の余白をつけています。

2-3. JavaScriptのコード解説

//href属性の「#」で始まるリンクを全て取得
const links = document.querySelectorAll('a[href^="#"]');
//取得したリンクを1つずつ処理を実行する
links.forEach((link) =>{
 //リンクをクリックしたら処理を実行する
  link.addEventListener('click', (e) => {
  //リンクイベントをキャンセルする
    e.preventDefault();
    //クリックしたリンクのhref属性を取得
    const href = link.getAttribute('href');
  //目的のセクションを取得
    const targetSection = document.querySelector(href);
    //画面の上からセクションのtop位置までの垂直方向の距離
    const sectionTop = targetSection.getBoundingClientRect().top;
  //現在位置を取得
    const currentPos = window.scrollY;
  //ヘッダーの高さ
    const gap = 84;
    //現在位置から目的のsectionまでのスクロール量
    const target = sectionTop + currentPos - gap;
  //特定の位置までスクロールさせる
    window.scrollTo({
      top: target,     //目的の位置のY座標を指定
      behavior: 'smooth', //スクロールの動きを指定
    });
  });
});

2行目:const links = document.querySelectorAll(‘a[href^=”#”]’); 

href属性が「#」で始まるリンクを全て取得しています。

4行目:links.forEach((link) =>{~};

取得したリンクをforEachで一つずつ取り出して処理を実行します。

7行目:e.preventDefault(); 

リンクのデフォルトのイベントをキャンセルする。

12行目:const targetSection = document.querySelector(href);

目的のセクションを取得します。

14行目:const sectionTop = targetSection.getBoundingClientRect().top;

getBoundingClientRect()はブラウザの左上を基点として対象要素の相対位置を返すメソッドです。targetSection.getBoundingClientRect().topはブラウザの上端からから目的のセクションの上端までの垂直方向の距離を取得します。

16行目:const currentPos = window.scrollY;

現在のスクロール位置を取得する。

18行目:const gap = 84;

headerの高さをgapとして設定しています。

19行目:const target = sectionTop + currentPos – gap;

現在位置から目的のセクションまでの距離。targetがどうしてgapを引いているのかを補足で解説してるのでよろしければご覧ください。

22-25行目:window.scrollTo({~});

特定の位置までスクロールさせる。

top: 目的位置までのY軸方向(垂直方向)のスクロール量を指定 

現在位置から目的のsection上端までの垂直方向のスクロール量を指定しています。

behavior:スクロールの動きを指定 

「smooth」を指定する滑らかに目的の位置までスクロールする。「auto」は目的の位置まで瞬時にスクロールする。

補足

targetはcurrentPosとsectionTopの合計からgapを引いていますが、このことについてもう少し深堀りしたいと思います。

例としてheaderのsection2のリンクを押してスクロールさせます。現在位置はスクロール量を0とします。

targetSection: section2

currentPos: 0

sectionTop: 250px(sectionの高さ) + 84px(headerの高さ)

section2までスクロールする場合、headerは位置が固定されているため本来は①の量だけスクロールしてほしいのですが、sectionTop(getBoundingClientRect().top)はブラウザの上端から目的のsection上端までの垂直方向の距離なので②の量をスクロールします。

つまり、headerの高さ分だけ余計にスクロールしています。そこでgap(headerの高さ分)を引くことでセクション2の上端がブラウザの上端にスクロールされるように調整しています。

式に当てはめてみると

const target = sectionTop + currentPos – gap;

const target = (250 + 84) + 0 – 84;

となります。

3. まとめ

今回はJavaScriptでスムーススクロールを実装する方法をご紹介しました。スムーススクロールは最終的にはCSSで実装することが多くなってくるのかなと思いますが、現時点ではJavaScriptで実装するほうが良いかもしれません。

ご参考にしていだけましたら幸いです。

関連記事