#概要
drawer.jsを使った、スマホ用バーガーメニュー実装で、新しいAndroidのChromeにおいて長いメニューがスクロールしない症状がありました。スクロールは、全くしない訳では無く、フリックで素早く動かすと若干スクロールするものの、通常のタップ移動では指を離したタイミングでメニューが閉じてしまいます。この不具合の修正を試みました。
#詳細
Androidでは、2016年末頃からスクロール動作を滑らかにするために、addEventListenerにイベントを登録する際、デフォルトでそのイベント内部の preventDefault を実行しないモードになりました。
jQuery の on() でイベントを登録している場合、addEventListener の第3引数に {passive: false}
を加えられるようなオプションは無いため、drawer.js内部で on()を使用している箇所をaddEventListener を使って書き直す必要があります。加えて何かとバグの多いiscroll.js ではAndroidのタッチ動作をうまく検出出来ていない様子なので、スマホだけに表示させる画面、という前提で動作するよう修正試みました。
#修正ポイント
drawer.js(v3.2.2)
19行目
iScroll.js のデフォルトオプションを定義している部分。preventDefaultが基本ONになるようにしている。
修正前
preventDefault: false
修正後
preventDefault: true,
107行目
修正前
if (touches) {
$this.on('touchmove.' + namespace, function disableTouch(event) {
event.preventDefault();
});
}
修正後
if (touches) {
document.addEventListener('touchmove.' + namespace, function disableTouch(event) {
event.preventDefault();
}, {passive: false});
}
iscroll.js(v5.2.0)
###42行目
内部でaddEventListnerを addEventとしてエイリアスを作っている部分があるので、ここで Android対応の書き方にする。
修正前
me.addEvent = function (el, type, fn, capture) {
el.addEventListener(type, fn, !!capture);
};
修正後
me.addEvent = function (el, type, fn, capture) {
el.addEventListener(type, fn, {passive: false});
};
###191行目
マウスポインタの場合の処理だが、スマホのみで使う前提なので、タッチイベントと同じものとして扱わせる。
修正前
pointerdown: 3,
pointermove: 3,
pointerup: 3,
修正後
pointerdown: 1,
pointermove: 1,
pointerup: 1,
###331行目
ポインター、マウスなどをdisableにする処理だが、条件分岐がAndroidの場合、望んだとおりにならないようなので、タッチデバイスしか扱わないのとChromeのデバッグ環境でも動くことを考えて修正。
修正前
disablePointer : !utils.hasPointer,
disableTouch : utils.hasPointer || !utils.hasTouch,
disableMouse : utils.hasPointer || utils.hasTouch,
修正後
disablePointer : false,
disableTouch : false,
disableMouse : false,
###482行目、622行目
e.preventDefault();をコメントアウト
これは
IScroll.prototype = {の中の
_start: と
_end: のpreventDefault を無効にしている。
以上で Android と iPhoneで正常動作するようになった。
#参考
-
Android Chrome beta (v.56) でdocumentに対するtouchイベントのpreventDefaultが効かない
(http://qiita.com/ru_shalm/items/4d79e94b5d9c7c88607d)
一連の根本的な原因、ChromeのaddEventListenerについての挙動について説明があります。 -
Passive Event Listeners によるスクロールの改善
(https://blog.jxck.io/entries/2016-06-09/passive-event-listeners.html)
さらに詳細な情報があって、理解が深まります。
この問題が起きているときに、Chromeブラウザのデバッグコンソールに以下のメッセージがでます。
Unable to preventDefault inside passive event listener due to target being treated as passive.