iOSSafariでキーボード出現⇒自動スクロールを止めたい

ioSSafariで、input要素にfocusが当たると、その要素を中央に寄せるように画面が自動でスクロールされます。

ちなみにアンドロイドだとビューポートの大きさが変わることで似たような挙動(focusされた要素を自動で中央にという挙動ではないが)をするらしいです。

これ自体はユーザビリティに貢献するのですが、ユーザビリティを害する場合もあります。

例えば、あるコメントに対してコメントしたいとき、元のコメントを参照しながら文章を打ちたいですよね。
でも勝手にスクロールされると、元のコメントが上の方へ消えてしまう。
ので、参照しながら文章を打ちたいところでは自動スクロールをキャンセルしたいんですね。

自動スクロール前のポジションに戻すことを考える


アンドロイドの、ビューポートのリサイズによる自動スクロールでは、全体を高さを初期(ready時の)ビューポートピクセル固定したdivで囲えば、そのdiv自体がスクロールされるので(でも高さを初期のビューポートにしてあるから)中身はスクロールされずに、事なきを得る、という感じの解決策があるらしいです。

まずはそういったハード面(CSS, metaタグ)での処理でキャンセルしたかったのですが出来なかったです。
で、iOSSafariでは「focusのinputを中央に寄せたい!」という意志を持った自動スクロールなので、その意志を逆のもので相殺させようと考えました。
具体的には、focusが当たる流れが
touchstart

(touchmove) * 0回以上

touchend

mouseover(初回時及び他の場所でタッチイベントがディスパッチされた後なら)

mousedown

focus

mouseup

click
らしいので(https://alpha.mixi.co.jp/entry/2012/10807/)

自動スクロール前(touchstart時)のポジションを取得して、自動スクロール後(mouseup時)にそのポジションまで戻すことを考えました。
んで、それは出来ました。
まぁあとで元のポジションに戻すだけなので、自動スクロールは一瞬してしまって、ユーザーからしたらどうしたどうした?って感じの挙動にはなってしまいます。

入力欄がキーボードで隠れる問題

なんですが、focusされたinputがキーボード裏に隠れてしまうんですね。
そのinput要素は、自動スクロール後にキーボードのすぐ上あるのですが、今回はその自動スクロールをキャンセルする上へのスクロールをその後に実行しているので、下の方へ潜ってしまうのです(iOS Safariでは、ソフトウェアキーボードが表示中にposition:fixedが効かない(absoluteのような挙動を示す)らしいです)。
input要素は、キャンセルのためのスクロール分だけ上に移動させればいいじゃんって思うじゃないですか。
じゃあどれだけキャンセルスクロールをかけたのか、touchstartとmouseup時のスクロール値の差を取ったんですね。
そしたら両者は同じ値だったんです(ただ、これは反映に時間がかかってるだけで、あとでsetTimeoutで300ms後に取るようにしたら別の値が取れました、ただ後述の$(window).scrollTop($(window).scrollTop())で相殺できるのは謎)。

簡単に例にまとめると、

touchstart時(スクロール位置500)

focusで自動スクロール
よって下にスクロールしてしまう

mouseup時にスクロール位置500のところまでスクロール

最初の位置に戻る


$(window).scrollTop($(window).scrollTop())って、今いる位置までスクロール(つまり、実質意味なし)っていう実行コードなのですが、これで自動スクロールが相殺されるという謎仕様。
あぁネイティブアプリ作りてぇ。
ブラウザの挙動制御とか難しいなと。


今日もお読みくださりありがとうございます。
ほぼ毎日、アプリ開発や事業に関する日記を書いておりますので是非またお読みになってください!

この記事が気に入ったらサポートをしてみませんか?