【JavaScript】スムーススクロールの解説

どうも、くまだです。

JavaScriptを使ったスムーススクロールの解説。自分なりの解釈やメモになります。

JavaScriptを使ったスムーススクロール

HTMLは以下のようになります。(CSSは今回関係ないので省略)

  <div class="l-container">
    <header class="l-header p-header">
      <div class="p-header__inner">
        <h1 class="p-header__title">
          <a href="/">
            <img src="" alt="" width="138" height="36">
          </a>
        </h1>
        <div class="p-header__hamburger">
          <button type="button" class="c-hamburger js-hamburger" aria-controls="navigation" aria-expanded="false"
            aria-label="メニューを開く">
            <span></span>
            <span></span>
            <span></span>
          </button>
        </div>
        <div class="p-header__nav js-nav-area" id="navigation">
          <nav class="p-nav" id="js-global-navigation" aria-label="グローバルナビゲーション">
            <div class="p-nav__inner">
              <ul class="p-nav__list">
                <li class="p-nav__item">
                  <a href="#section-a" class="p-nav__link">a</a>
                </li>
                <li class="p-nav__item">
                  <a href="#section-b" class="p-nav__link">b</a>
                </li>
                <li class="p-nav__item">
                  <a href="#section-c" class="p-nav__link">c</a>
                </li>
                <li class="p-nav__item">
                  <a href="#section-d" class="p-nav__link">d</a>
                </li>
                <li class="p-nav__item">
                  <a href="#section-e" class="p-nav__link">e</a>
                </li>
              </ul>
            </div>
          </nav>
        </div>
      </div>
    </header>

    <main class="p-main">
      <section class="p-section" style="background-color: red;" id="section-a">
        <h2>セクションa</h2>
      </section>
      <section class="p-section" style="background-color: blue" id="section-b">
        <h2>セクションb</h2>
      </section>
      <section class="p-section" style="background-color: green;" id="section-c">
        <h2>セクションc</h2>
      </section>
      <section class="p-section" style="background-color: orange;" id="section-d">
        <h2>セクションd</h2>
      </section>
      <section class="p-section" style="background-color: yellow;" id="section-e">
        <h2>セクションe</h2>
      </section>
    </main>
    <footer>
      <ul class="p-nav__list">
        <li class="p-nav__item">
          <a href="#section-a" class="p-nav__link">a</a>
        </li>
        <li class="p-nav__item">
          <a href="#section-b" class="p-nav__link">b</a>
        </li>
        <li class="p-nav__item">
          <a href="#section-c" class="p-nav__link">c</a>
        </li>
        <li class="p-nav__item">
          <a href="#section-d" class="p-nav__link">d</a>
        </li>
        <li class="p-nav__item">
          <a href="#section-e" class="p-nav__link">e</a>
        </li>

      </ul>
    </footer>

  </div>
document.addEventListener('DOMContentLoaded', function () {
  const smoothScrollTrigger = document.querySelectorAll('a[href^="#"]');
 
  function smoothScroll(targetId) {
    const targetElement = document.getElementById(targetId);

    if (targetElement) {
      const header = document.querySelector('.p-header');
      const headerHeight = header.clientHeight;
      const targetRect = targetElement.getBoundingClientRect().top;
      const offset = window.scrollY;
      const target = targetRect + offset - headerHeight;

      window.scrollTo({
        top: target,
        behavior: 'smooth',
      });
    }
  }
  smoothScrollTrigger.forEach(trigger => {
    trigger.addEventListener('click', function (e) {
      e.preventDefault();
      const href = trigger.getAttribute('href').replace('#', '');

      smoothScroll(href);

    });
  });
});

JavaScriptの解説

以下の記述で、href属性が#で始まるaタグを全て取得します。

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

以下のループで、各aタグにクリックイベントを追加します。

foreach文は、

  • (配列).foreach((各要素) => (各要素に対して行う関数))

のような記述になります(なお、querySelectorAllの返り値はは配列ではない)。

これを当てはめると、以下のようになります。

  smoothScrollTrigger.forEach(trigger => {
    trigger.addEventListener('click', function () {
    // ここにクリックイベント書く

  
    });
  });

クリックイベント内には以下のように記述します。

getAttributeでhref属性の値を取得します。例えば、<a href=”#section-b”>をクリックしたら「#section-b」が取得されます。

これを、replace(‘#’, ”)で「#」文字を削除します。つまり取得した「#section-b」の「#」が取り除かれて「section-b」になり、それを変数「href」に格納します。

その変数を後述するsmoothScroll関数に渡します。

  smoothScrollTrigger.forEach(trigger => {
    trigger.addEventListener('click', function () {
      
      const href = trigger.getAttribute('href').replace('#', '');

      smoothScroll(href);

    });
  });

smoothScroll関数作成

smoothScroll関数は以下のように記述していきます。

smoothScroll関数の()内には「href」が渡されます。ここでは「targetId」として受け取ります。

  function smoothScroll(targetId) { // targetIdに変数hrefが渡される
  // ここに処理を書く
   const targetElement = document.getElementById(targetId);

  }

イメージとしては、下のような状態です。<a href=”#section-b”>をクリックした場合は、変数hrefの中には「section-b」が格納されています。

  function smoothScroll(href) { 
  // ここに処理を書く
   const targetElement = document.getElementById(href);
  }

// ↓ <a href="#section-b">をクリックした場合

  function smoothScroll(section-b) { 
   const targetElement = document.getElementById(section-b);
  }

getElementById()でそのidを持つ要素を取得します。それを変数targetElementに格納します。

次に、もしその要素が存在するならif文以下の処理になります。

  • p-header要素を取得して変数headerに格納
  • 変数headerの「paddingのみを含んだ高さ」をclientHeihtで取得
  • 変数targetElementの「ビューポートに対する相対位置」をgetBoundingClientRect().topで取得して変数targetRectに格納
  • 変数offsetにwindow.scrollYで取得した「現在のスクロール位置」を格納
  function smoothScroll(targetId) {
    const targetElement = document.getElementById(targetId);


    if (targetElement) { // もしtargetElement が存在するなら

      const header = document.querySelector('.p-header');
      const headerHeight = header.clientHeight;
      const targetRect = targetElement.getBoundingClientRect().top;
      const offset = window.scrollY;
      const target = targetRect + offset - headerHeight;


    }
  }

イメージとしては以下のような感じです。

変数targetには、

  • ページのスクロール量+ビューポートから要素までの距離

を格納しています。固定ヘッダーがある場合は、変数headerを高さを引いてあげます。

次にwindow.scrollTo()でウィンドウを指定した位置にスクロールさせます。topにはスクロール先の縦方向のピクセル位置を指定します。ここでは、変数targetを指定します。

behaviorオプションのsmooth指定でスムーススクロールになります。

window.scrollTo({
  top: target,
  behavior: 'smooth',
});

現状だと、aタグをクリックすると一瞬で該当のセクションに移動するので、以下の処理を追記します。e.preventDefault();がないと、デフォルトのページジャンプが起こり、スムーススクロールが行われません。

e.preventDefault();を追記するとそのデフォルトのページジャンプ動作を防ぐことができます。デフォルトのページジャンプを無効化して、smoothScroll関数でスムーススクロールさせるイメージです。

  smoothScrollTrigger.forEach(trigger => {
    trigger.addEventListener('click', function (e) { // e追記

      e.preventDefault(); // 追記
      const href = trigger.getAttribute('href').replace('#', '');

      smoothScroll(href);

    });
  });

ここまで読んでくださりありがとうございました。

この記事を書いた人