drawer.js アンドロイドでメニューがスクロールしない 

腹立たしいほどハマってしまったので、記録したい。
元は、iScroll.js と drawer.js のハンバーガーメニューがAndroidで動かない
https://qiita.com/YujiHatanaka/items/64b6a6c8facb3a2f69b5

内容——–

iScroll.js と drawer.js のハンバーガーメニューがAndroidで動かない
JavaScript
iscroll.js
概要

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になるようにしている。
修正前
drawer.js

preventDefault: false

修正後
drawer.js

preventDefault: true,

107行目

修正前
drawer.js

if (touches) {
$this.on(‘touchmove.’ + namespace, function disableTouch(event) {
event.preventDefault();
});
}

修正後
drawer.js

if (touches) {
document.addEventListener(‘touchmove.’ + namespace, function disableTouch(event) {
event.preventDefault();
}, {passive: false});
}

iscroll.js(v5.2.0)
42行目

内部でaddEventListnerを addEventとしてエイリアスを作っている部分があるので、ここで Android対応の書き方にする。
修正前
iscroll.js

me.addEvent = function (el, type, fn, capture) {
el.addEventListener(type, fn, !!capture);
};

修正後
iscroll.js

me.addEvent = function (el, type, fn, capture) {
el.addEventListener(type, fn, {passive: false});
};

191行目

マウスポインタの場合の処理だが、スマホのみで使う前提なので、タッチイベントと同じものとして扱わせる。

修正前
iscroll.js

pointerdown: 3,
pointermove: 3,
pointerup: 3,

修正後
iscroll.js

pointerdown: 1,
pointermove: 1,
pointerup: 1,

331行目

ポインター、マウスなどをdisableにする処理だが、条件分岐がAndroidの場合、望んだとおりにならないようなので、タッチデバイスしか扱わないのとChromeのデバッグ環境でも動くことを考えて修正。

修正前
iscroll.js

disablePointer : !utils.hasPointer,
disableTouch : utils.hasPointer || !utils.hasTouch,
disableMouse : utils.hasPointer || utils.hasTouch,

修正後
iscroll.js

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.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA