🍆

スムーススクロールが終了してから任意のコードを実行する方法

2023/05/29に公開

スムーススクロールが完全に終わってから何かをしたいことはありませんか?
例えば、ボタンをクリックしたら裏側では読み込みを行い、スムーススクロールでトップに戻り、スムーススクロールが終了したら結果を出力する...みたいなやつ。

つまり、スクロールの完了を待つ方法ということですね。

上記にサンプルを用意したので動かしながら確認してみてください。
※Zennで埋め込みを動かすとZennのページ自体もスクロールされちゃうのでCodePenで直接確認するのを推奨します。

スクロール完了を待つ関数

let scrollResolveTimeout;

const waitForScrollComplete = () => {
  return new Promise((resolve) => {
    const scrollTimeout = setTimeout(() => {
      resolve(true);
    }, 100);
    const scrollTimeoutEvent = addEventListener('scroll', function fn (e) {
      clearTimeout(scrollTimeout);
      clearTimeout(scrollResolveTimeout);
      scrollResolveTimeout = setTimeout(function () {
        console.log('スクロールが完了しました!');
        removeEventListener('scroll', fn);
        resolve(true);
      }, 100);
    });
  });
}

ポイントは let scrollResolveTimeout; の位置です。
関数外に置くことでaddEventListenerが重複せずに済んでいます。

    const scrollTimeout = setTimeout(() => {
      resolve(true);
    }, 100);

こちらのコードは何気に大切で、スクロールイベントが発火しないところでスムーススクロールが発生したときに100msで無条件で完了扱いにする役割を持ちます。(スクロールが発火する場合はこの無条件終了は clearTimeout(scrollTimeout); によって無視されます。)

実行をする

const gotoBottomTrigger = document.querySelector('.goto-bottom');
const gotoTopTrigger = document.querySelector('.goto-top');

// ページ最下部へ進む
gotoBottomTrigger.addEventListener('click', async function (e) {
  document.querySelector('.here-bottom').scrollIntoView({
    behavior: 'smooth'
  });
  await waitForScrollComplete();
  alert('最下部に到達しました!');
});

// ページ最上部に戻る
gotoTopTrigger.addEventListener('click', async function (e) {
  document.querySelector('.here-top').scrollIntoView({
    behavior: 'smooth'
  });
  await waitForScrollComplete();
  alert('ページの一番上に到達しました!');
});

scrollIntoView でスムーススクロールを開始し、 await waitForScrollComplete(); で待機し、完了したら alert() が実行されるという流れです。

とても単純なコードで実現できますね!

Discussion