スクロール禁止のJavaScriptについて考えてみる【PC/スマホ対応】

「モーダルを開いた時にスクロールを一時的に禁止させたい」という目的でmousewheelイベントを使ったところ、Firefoxブラウザで動作しないということがありました。

その調べ物ついでに、スクロール禁止関連のイベントで気になったことを調べたのでまとめました。
主要ブラウザで動作するコードと併せて紹介します。

初めに使おうとしたのは調べた際によく見かけたこちらのコード。

// スクロール禁止
function ban_scroll() {
	// PC用
	document.addEventListener("mousewheel", notscroll, { passive: false });
	// スマホ用
	document.addEventListener("touchmove", notscroll, { passive: false });
}
// スクロール禁止解除
function go_scroll() {
	// PC用
	document.removeEventListener("mousewheel", notscroll, { passive: false });
	// スマホ用
	document.removeEventListener("touchmove", notscroll, { passive: false });
}

// スクロール禁止関数
function notscroll(e) {
	e.preventDefault();
}

WindowのEdge、IE11、Chrome、Firefox/Mac safari、Chrome、Firefoxの最新バージョン、AndroidOS(バージョン10)、iOS(バージョン14)の実機でチェックしたところ、Firefoxブラウザーではmousewheelイベントがそもそもサポートされていなかったようです。
さらに、mousewheelイベント自体がすでに非推奨とのこと。

参考)Element: mousewheel event – Web APIs | MDN

mousewheelイベントの代わりにwheelイベントを使う

MDNドキュメントのwheelイベントに「このイベントは標準外、非推奨の mousewheel イベントを置き換えるものです。」と書いてある通り、現在はmousewheelイベントの代わりにwheelイベントを使うことが推奨されています

両者の関係はというと、IE6でまずmousewheelイベントが実装され、その後勧告されたDOM Level 3でwheelイベントが実装、mousewheelイベントが非推奨になったという背景だったようです。
当初はサポート状況との兼ね合いで古いブラウザ用にmousewheelイベントで対応する必要があったようですが、それもSafari7以下とIE8以下というかなり古いものなので、現在の主要ブラウザはFirefoxも含めサポートされています
実際上記ブラウザで確認した限りでは問題なく動作しました。

参考)ホイール系イベント2014年版クロスブラウザ

ちなみに、wheelイベントとmousewheelイベント両方をaddEventListenerで登録した場合、こちらの記事によると共存せずwheelイベントだけ発火するようです。(chromeの場合)

Google Chromeでwheelイベントとmousewheelイベントは共存しない話 – Qiita

Firefoxで使えるDOMMouseScrollとは?

「Firefox mousewheelが効かない JavaScript」などで調べると、よくDOMMouseScrollイベントを使おう!といった英語の記事がでてきます。

こちらは何かと調べたところ、Firefox向けmousewheelイベントのようなもの。
Firefoxでしか動作せず、また古いイベントのため、mousewheelイベントと同様現在は非推奨となっています。
ちょっと試してみたところ、前述コードにDOMMouseScrollを差し替えてスクロール禁止処理をすると、カクカクしながらスクロールするという不安定な挙動がみられました。

参考)Element: DOMMouseScroll event – Web APIs | MDN

scrollイベントでは代用できない

なんとなくスクロール禁止処理はwheelやらtouchmoveを使うべきと認識していたけれど、実際にscrollでは代用できないのか?と思ったのでメモ。

結論から言えば代用はできません。
scrollはキャンセル可能なイベントではなく、スクロール禁止処理自体が行えないためです。

イベントがキャンセル可能かどうかは event.cancelable を使って確認できます。キャンセル不可能なイベントに対して preventDefault() を呼び出しても効果はありません。

引用元)Event.preventDefault() – Web API | MDN

こちらに倣って実際にそれぞれのイベントの中身を見てみるとこんな感じ。


wheelイベント


touchmoveイベント


scrollイベント(cancelableがfalseになっている=キャンセル不可)

これまでpreventDefault()が効く・効かないイベントを感覚的に捉えていたんですが、event.cancelableで判断できるんですね。
scrollはなんとなくキャンセル処理できるだろうと思っていたので、これを見て納得でした。

まとめ

色々と遠回りしましたが、結論としては前述のコードのmousewheelをwheelにすれば現在の主要ブラウザを網羅できそうです。

// スクロール禁止
function ban_scroll() {
	// PC用
	document.addEventListener("wheel", notscroll, { passive: false });
	// スマホ用
	document.addEventListener("touchmove", notscroll, { passive: false });
}
// スクロール禁止解除
function go_scroll() {
	// PC用
	document.removeEventListener("wheel", notscroll, { passive: false });
	// スマホ用
	document.removeEventListener("touchmove", notscroll, { passive: false });
}

// スクロール禁止関数
function notscroll(e) {
	e.preventDefault();
}

ちなみに今回は{ passive: false }を使用していますが、こちらに関して紹介している記事もあるのでよかったらご覧ください。