IOSキーボードがfocusoutイベントでしまわれたときに元の場所に戻らない問題を解決する
問題の症状
本日、モバイルH5ページを開発中、IOSでキーボードをしまった時にインターフェイスが元の位置に戻らないという問題に遭遇しました。その問題と症状について、以下に詳しく説明します。
ページ構成
当該ページは、フォーム構造になっています。つまり、divの下に4つの入力フォームがあり、ユーザーが郵送情報を記入するようなものです。のようなものです。
<div>
<input type="text" placeholder="Please fill in the province, city, and county" />
<input type="text" placeholder="Please fill in the address" />
<input type="text" placeholder="Please fill in your name" />
<input type="text" placeholder="Please fill in the contact phone number" />
</div>
スクリーンショットは以下の通りです。
キーボードがポップアップしたときに自動的にページが上に移動する
ユーザーが電話で連絡先を入力すると、アイフォンのキーボードがポップアップし、ページ全体がアイフォンの上に移動して、ユーザーが電話の入力ボックスを見られるようになります (そうしないと、キーボードが電話の入力ボックスを覆ってしまいます)。このとき、ページの上部は、実際にはビューポートから一部外れています (入力ボックスの列がインターフェースから消えているのが見えます)。
キーボードをしまったときに、ページが元の位置に戻らない
しかし、ユーザーが入力を終えてキーボードを閉じると、キーボードはしまわれますが、ページの位置は元に戻りません。
問題分析
これは、IOSがキーボードをしまっているときに、ページを落ちないようにビューポートからロールアウトすることができないために起こります。このとき、ユーザーはページを指でドラッグして戻すことができます。
この問題を解決するために
window.scrollTo(0, 0)
を使用してページをスクロールし、ビューポートの上部に合わせることで、ホーミング効果を実現します。
そこで問題は、フォームの 4 つの入力ボックスすべてに blur イベントを追加し、ハンドラで
window.scrollTo
をハンドラで実行します。しかし、Vueの
@blur
やDOM操作で、4つのイベントリスナーを追加するのは、あまりエレガントではありません。当然、イベントプロキシーの利用を考えた。
イベントプロクシ
つまり、イベントリスナーをトップエレメントに配置し、inputBlur関数を定義してトリガーを待つのです。
<div @blur="inputBlur">
<input type="text" placeholder="Please fill in the province, city and county" />
<input type="text" placeholder="Please fill in the address" />
<input type="text" placeholder="Please fill in your name" />
<input type="text" placeholder="Please fill in the contact phone number" />
</div>
その結果、イベントリスナーがトリガーされないことが判明しました。この原因は、入力ボックスの
blur
イベントがバブリングしていませんでした。
バブル化できない場合の解決策
クエリした結果
focus
と
blur
2つのDOMイベントは、仕様上、バブリーでないだけです。同様に、他の 2 つのイベント
focusin
と
focusout
がバブリーになっている。
ウェブ上のいくつかの記事で
focusin
と
focusout
は、IE のみでサポートされている DOM イベントです。実際、MDN のドキュメントを見ると、この 2 つのイベントは DOM 3 仕様で標準化され、かなりの数のブラウザでサポートされていることがわかります。
そこで、この2つのイベントの問題を決定的に解決するために、次のように変更します。
focusout
<div @focusout="inputBlur">
<input type="text" placeholder="Please fill in the province, city and county" />
<input type="text" placeholder="Please fill in the address" />
<input type="text" placeholder="Please fill in your name" />
<input type="text" placeholder="Please fill in the contact phone number" />
</div>
inputBlur(e) {
// First, determine if the target element that triggered the event is an input input box, we'll just focus on the behavior of the input box.
if (e && e.target && e.target.tagName && e.target.tagName.toLowerCase() === 'input') {
window.scrollTo(0,0);
}
},
次に、イベントハンドラを実装します。
phone input box
この時点で問題は解決し、入力ボックスから入力し、キーボードの完了をクリックしてキーボードを片付けると、期待通りの効果が得られます。
しかし、モバイルでテストしてみたところ、このように
Name input box
に直接切り替わります。
Phone input box
この動作により、ページがカクカクする。分析を続けましょう。
ジッター問題の解決
2つの入力ボックスを切り替えたときにカクカクする原因は、とてもシンプルです。なぜなら、上記の2つの入力ボックスの間を切り替えると、ページでは最初に
blur
の
Name input box
イベントが発生し、それが
focus
の
window.scrollTo(0,0)
イベントを使用します。この場合、ブラーは私たちの
Name input box
でページを少しスクロールさせ、その後に
window.scrollTo
がフォーカスされるため、キーボードがポップアップし続け、その結果、ページが再び上に移動します。
実際、このように2つの入力ボックスを切り替えて使う場合、入力ボックスの前にある
<div @focusout="inputBlur" @focusin="inputFocus">
<input type="text" placeholder="Please fill in the province, city and county" />
<input type="text" placeholder="Please fill in the address" />
<input type="text" placeholder="Please fill in your name" />
<input type="text" placeholder="Please fill in the contact phone number" />
</div>
の動作は、1つ目の入力ボックスがぼやけたときに発生します。そこで、切り替え時に1つ目の入力ボックスの動作をカットするようにコードを修正しましょう。ここでは、setTimeoutを使って修正します。
inputBlur(e) {
// First, determine if the target element triggering the event is an input input box, we'll just focus on the behavior of the input box.
if (e && e.target && e.target.tagName && e.target.tagName.toLowerCase() === 'input') {
// The input box loses focus, to restore the scrolling part of the page that pushed the IOS keyboard out. To align the page to the top of the viewport
console.log('settimer')
this.timer = setTimeout(() => {
console.log('timer triggered')
window.scrollTo(0,0);
}, 0)
}
},
inputFocus(e) {
// If focus, remove the timer from the previous input box
if (e && e.target && e.target.tagName && e.target.tagName.toLowerCase() === 'input') {
clearTimeout(this.timer);
}
}
inputBlur(e) {
// First, determine if the target element triggering the event is an input input box, we'll just focus on the behavior of the input box.
if (e && e.target && e.target.tagName && e.target.tagName.toLowerCase() === 'input') {
// The input box loses focus, to restore the scrolling part of the page that pushed the IOS keyboard out. To align the page to the top of the viewport
console.log('settimer')
this.timer = setTimeout(() => {
console.log('timer triggered')
window.scrollTo(0,0);
}, 0)
}
},
inputFocus(e) {
// If focus, remove the timer from the previous input box
if (e && e.target && e.target.tagName && e.target.tagName.toLowerCase() === 'input') {
clearTimeout(this.timer);
}
}
以上、本記事の全内容をご紹介しましたが、皆様の学習のお役に立てれば幸いです。また、Script Houseをより一層応援していただければ幸いです。
関連
-
HTMLスケッチのためのEmmet構文ルールの実装
-
CAPTCHAを生成するHTML5サンプルコード
-
モバイル適応のためのremやviewportの使い方を説明する。
-
キャンバス描画の解像度が拡大され、ぼやけた状態になる
-
localStorageの最大記憶容量を取得する方法を説明する
-
canvas.toDataURL()エラーの詳細な解決策はすべてこちら
-
HTML5によるアプリケーションキャッシュの実装
-
カスタムお絵かきボード用JavaScript+Canvasサンプルコード
-
html5 目覚ましアプリミニノート
-
html2canvas.jsを使用してページのスクリーンショットを撮影し、表示またはアップロードするサンプルコード
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン