スクロールイベントをユーザー操作だけに限定したい時のケア

こんにちは、ユアサです。
今回はJSで実装されているaddEventListenerscrollイベントをユーザーの操作時のみのアクションとして制御する手法について備忘録も兼ねて紹介します。

記事を書くに至った経緯

addEventListenerscroll、いわゆるスクロールイベントと呼ばれる発火方法ですが、文字通りページ内のスクロールの有無を判定するものです。実装するのは簡単ですが、思い通りの挙動にさせるには少しケアをしてあげないといけない場面がありました。

例えると、「ページトップへ戻る」のようなボタンをスクロールイベントで表示させるとします。

See the Pen scroll_pagetop_unplanned01 by felly (@felly00505) on CodePen.

このボタンの仕様として、下記の条件は満たすよう制作します。

  • ページトップにいる時は非表示
  • ある程度スクロールしたら表示
  • クリックしたらページトップへ移動、その後に再度非表示

文字通りページトップへ移動するためのボタンなので、ボタン自体はこの程度の仕様で事足ります。
しかし外部サイトからのアンカーリンク付きURLのような、ページ読み込み後の初期位置がページトップでない場合は、「ページトップへ戻る」ボタンは最初から表示された状態になってしまいます。

↓以下URLから見てもらうと分かるように、何も触ってなくても勝手にボタンが表示されてしまいます。 https://es-d-5792135220260406-019d52a8-da42-7baf-8bb3-f88c5fe90b89.codepen.dev/#section02

このように、addEventListenerscrollイベントはユーザー操作以外で予期せず発生する場面があります。知らなかった・・・・そうなんだ・・・・ということで今後のための備忘録として記事とするに至りました。

スクロールの種類をどのように判定するか

実はaddEventListenerのユーザー操作系のイベントにはwheel touchmove touchstart keydownなどが用意されています。なんて便利。
ユーザー操作のスクロールに限定したいのであれば、scrollイベントを使うのではなくこれらを使えばいいじゃない。

See the Pen scroll_pagetop_unplanned02 by felly (@felly00505) on CodePen.

・・・で解決すれば良いのですが、今度はページ内でのアンカーリンククリック後の移動時には発火しなくなってしまいます。

そのため、組む際の考え方としては以下のようになります。

  • スクロールしたかの判定は欲しいのでscrollイベントを基軸にする
  • scrollイベント発火後、どの操作でページがスクロールしたのか判定

これを踏まえて、先ほどのコードを改良してみます。

See the Pen scroll_pagetop_fixed by felly (@felly00505) on CodePen.

まずページ表示後の初期位置を取得します。

// ページ表示時の初期位置を取得
window.addEventListener('load', () => {
  var windowNowPlace = window.pageYOffset
  onScrollEvent(windowNowPlace)
})

取得した初期位置が、scrollイベント発火後の位置とイコールか判定させます。これで実質的に「ユーザー操作でスクロールした時に発火させる」条件に絞ることができました。

// 画面がスクロールした時の判定
function onScrollEvent(windowNowPlace) {
  window.addEventListener('scroll', () => {

    // ユーザー操作判定用の変数がtrueの場合
    if (isUserScrolling) {

      // スクロール後のY座標 = ページ読み込み後の初期位置と違う場合
      if (!(this.window.pageYOffset == windowNowPlace)) {
        if (window.scrollY > positionY) {
          html.classList.add('is-scrolling');
        } else {
          html.classList.remove('is-scrolling');
        }
      }
    }
  },
  {
    passive: true
  })
}

↓上記コードを反映させた外部リンクです。遷移後に「ページトップへ戻る」ボタンが非表示になっているかと思います。
https://es-d-4723097020260407-019d58ef-c40b-7869-aecb-a0dfa164656f.codepen.dev/#section02

最後に

方法の紹介というより、厳密には実装する際の認識の紹介でしたね。
必ずこうしたケアをしないといけないわけではないですが、極力ユーザーライクな作りを目指すためにも常に頭の片隅に置いておきたいと思います。