1. ホーム
  2. Web制作
  3. html5

IOSキーボードがfocusoutイベントでしまわれたときに元の場所に戻らない問題を解決する

2022-01-31 08:20:52

問題の症状

本日、モバイル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をより一層応援していただければ幸いです。