## 注意
この記事はAndroid版Google Chrome beta(56.0.2924.23)を元に記述しています。
betaなので今後のChromeのアップデートで挙動が変わる可能性があります。
三行でおk
- パフォーマンスのため
window
とかdocument
に対するtouchXXX
は、preventDefaultが無効になる -
addEventListener
時に明示的に{passive: false}
を指定すればOK - いも
どういうわけか
document.addEventListener('touchstart', function (e) {
e.preventDefault();
// 以下、楽しい処理
});
みたいなコードを書くと、画面をスクロールできないようになります。
ゲームなど、画面まるごと <canvas>
みたいなときにはスクロールされても困るので、
こういうことをよくやったりしますね。1
が、 Android Chrome beta (56) で、思ったように動かなくなりました。
Android Chrome beta(56)でスワイプ操作がブラウザに吸われて泣いてる。document に対する touchstart とかの preventDefault 効いてないんかな……なぞ pic.twitter.com/nZhF4NXuZL
— Ruたん (@ru_shalm) 2016年12月16日
Chrome(55)ではスワイプしても大丈夫だったものが、
Chrome beta(56)ではキャンセルされず、
ブラウザのリロードアクションが発火している様子です。
つらみ。
原因
コンソールに以下のようなwarningが表示されています。
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
つまり、どゆこと?
上のサイトに詳しく説明がされているのですが、
「addEventListener
のコールバック内で
preventDefault
が呼ばれるか確認してからじゃないと、
画面をスクロールしていいのかわからない!」
というのがパフォーマンス上の問題になっており、
preventDefault
を確認しない(= preventDefault
が呼ばれないものとする)オプションが
addEventListener
に追加されたそうです。
// preventDefaultを呼ばないのでスクロールをキャンセルしないのだけど、
// 実際に処理を実行しないとそれがわからないため、スクロールが遅延する
document.addEventListener('touchstart', function (e) {
同期的でめっちゃ重すぎる処理();
});
// 第三引数のオプションに passive: true を渡すと、
// 中に preventDefault が存在しない扱いになる(=呼んでも意味ない)ので、
// 処理を実行せずにスクロールを開始できる
document.addEventListener('touchstart', function (e) {
同期的でめっちゃ重すぎる処理();
}, { passive: true });
passive
のデフォルト値は false
なのですが、Google先生いわく
「そもそも root 要素みたいなもので preventDefault 使うこと普通ないよな?
快適なインターネットのためにデフォルトで passive
を true
にするぞ!」
とのことらしいです。
確かにページをスクロールさせたくない、っていう需要は普通はない。
僕はゲームだからある……(◞‸◟)
どうしたらいいのか
- まずホントに
document
などでpreventDefault
する必要があるか考える- スクロールが遅延するのは事実
- 必要なのであれば第三引数に
{passive: false}
を明示的に渡す
そもそも beta での挙動なので変わるかもですが、
対応が必要な方は頑張ってください。
-
Android Chromeの場合、上から下にスワイプするとリロードかかっちゃうのでゲームが死ぬ ↩