Posted at

Android Chrome beta (v.56) でdocumentに対するtouchイベントのpreventDefaultが効かない

More than 1 year has passed since last update.


 注意

この記事は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) で、思ったように動かなくなりました。

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 使うこと普通ないよな?

 快適なインターネットのためにデフォルトで passivetrue にするぞ!」

とのことらしいです。

確かにページをスクロールさせたくない、っていう需要は普通はない。

僕はゲームだからある……(◞‸◟)


どうしたらいいのか


  • まずホントに document などで preventDefault する必要があるか考える


    • スクロールが遅延するのは事実



  • 必要なのであれば第三引数に {passive: false} を明示的に渡す

そもそも beta での挙動なので変わるかもですが、

対応が必要な方は頑張ってください。





  1. Android Chromeの場合、上から下にスワイプするとリロードかかっちゃうのでゲームが死ぬ