はじめに
ブラウザの上スクロールでリロードされることを防ぎたいと思ったことはありませんか?特にフォーム入力中やデータ編集中に、意図せずリロードが発生してしまうと、ユーザーの作業が消えてしまいます。この記事では、そのような問題を可能な限り抑える実装方法を紹介します。
必ずスクロールによるリロードを制限できるものではありません。
Androidでリロード可能な場合があることを確認しています。
リロードで消えたら困るものに関しては、リロード時に確認を挟むような処理を追加しましょう。
実装のポイント
-
上方向のスクロールのみを制御
この実装の特徴は、全てのスクロール動作を制限するのではなく、上方向のスクロールのみを制御する点です。これにより、通常のスクロール操作に影響を与えることなく、意図しないリロードを防ぐことができます。 -
CSSによる制御
overscroll-behavior: none; というCSSプロパティを使用することで、ブラウザのデフォルトのオーバースクロール動作を制御します。このプロパティは、スクロールチェーンなどを防ぐために使用されます。 -
スクロールイベントを使用
JavaScriptのscrollイベントを使用して、ユーザーのスクロール位置を常に監視します。これにより、適切なタイミングでoverscroll-behaviorの切り替えを行うことができます。
コードの詳細な説明(Vue.jsのコード)
私はVue.jsを用いて開発をする機会が多いのでVue.jsのコードを用いますが、基本的なJavaScriptの側面が大きく、HTML,CSS,JSや、Reactなどの場合でも問題なく使用できるものです。
// 上方向へのスクロールを制限する処理
const blockScrollUp = () => {
// 1pxに設定していますが、縦幅に余裕があるページは
// もっと大きめに設定した方が安定します。
if (document.documentElement.scrollTop <= 1) {
document.documentElement.style.overscrollBehavior = 'none'
} else {
document.documentElement.style.overscrollBehavior = 'auto'
}
}
スクロール位置が最上部(1px以下、つまり画面の一番上にいる時)の場合にoverscroll-behavior: none;を適用し、それ以外の場合はautoに設定します。
// コンポーネントがマウントされたタイミングで実行
onMounted(() => {
window.addEventListener('scroll', blockScrollUp)
// 初期化時にも実行(最初はスクロールされていない状態のため、
// 処理が実行されずリロードができてしまう為)
blockScrollUp()
})
コンポーネントのマウント時に、スクロールイベントのリスナーを登録します。また、初期状態を設定するためにblockScrollUpを即時実行します。
onBeforeUnmount(() => {
window.removeEventListener('scroll', blockScrollUp)
})
コンポーネントのアンマウント時には、メモリリークを防ぐためにイベントリスナーを解除します。
注意点
- スクロールイベントだけでカバーしきれない場合:
setIntervalを使用すると、実行間隔を狭めすぎた場合にパフォーマンスに影響を与える可能性がありますが制限の精度を高めることができます。requestAnimationFrameなどを使用しても良いと思います。
- CSSのみでの実装:
下スクロールも制限してよい場合や、空白ができても問題ない場合は、以下のようなCSSだけで実装することも可能です
html {
overscroll-behavior: none;
}
「空白ができても問題ない場合は」と上で書きましたが、CSSだけで実装するとブラウザのツールバー部分が空白(ピンクの部分)ができてしまいますので、許容できる範囲であればCSSで1行追加するだけで済むのでとても簡単だと多います。
サンプルページ
開発者ツールだと正常な動きが確認できないと思いますが、ブラウザのサイズを変更して、ご確認いただくとなんとなくのイメージができるかと思います。
まとめ
この実装により、ユーザーの意図しないリロードを防ぎつつ、通常のスクロール操作への影響を最小限に抑えることができます。特にフォーム入力やデータ編集を伴うWebアプリケーションにおいて効果的なアプローチになるかと思いますので適切に使用していただければと思います。