検索すると overflow:hidden や preventDefault(); を使うとスクロールを無効化することができるといった情報が出てきますが、iOS や Chrome 54 以降では動作しなくなっています。
preventDefault(); が動作しない理由
Chrome 54 から動作しなくなったようです:
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
https://www.chromestatus.com/features/5093566007214080
https://developers.google.com/web/updates/2017/01/scrolling-intervention
ブラウザはスクロール時に起こるイベント (touchmove
など) が起こるとき、addEventListener
などによって処理が登録されていると、その処理に preventDefault();
(スクロールをキャンセルする関数)が含まれているかどうか確認します。この確認している間はスクロールは行われません。そのため、時間がかかる処理がある場合は、スクロール速度が低下して動作がカクカクになってしまうという問題がありました。
そこで passive
というオプションが追加されました。
このオプションを true
にすると、「この処理に preventDefault();
は含まれていないのでスクロールを行っていいですよ」とブラウザに明示することができます。
これによりブラウザは処理を行う前にスクロールをすることができるので、スクロール速度を改善することができます。
比較動画はこちら (左が従来のもの、右が {passive: true}
を指定したものです):
Side by side: passive event listeners
https://youtu.be/65VMej8n23A
Chrome 54 以降ではデフォルトでこの passive
が true
となっており、preventDefault();
を使用する場合には {passive: false}
を指定しなくてはなりません。
対処法
addEventListener
の第3引数として {passive: false}
を指定する。
document.addEventListener('touchmove', function(e) {e.preventDefault();}, {passive: false});
これはモバイルでしか動作しないため、デスクトップ用に overflow: hidden
も必要です。
まとめ
html, body{
overflow: hidden;
}
document.addEventListener('touchmove', function(e) {e.preventDefault();}, {passive: false});
おまけ
touch-action
というすべてのタッチ操作を無効化する CSS プロパティもあります。
今後はこちらを使っていくことが推奨されていますが、Safari はまだ対応していません。
touch-action: none;
iOS Safari only supports
auto
andmanipulation
.
https://caniuse.com/#feat=css-touch-action
参考
Passive Event Listeners によるスクロールの改善
https://blog.jxck.io/entries/2016-06-09/passive-event-listeners.html
iOSはoverflow:hidden;でスクロールを無効にできない - Qiita
https://qiita.com/mimoe/items/f5f668cebb697d073553