かぁくん本紀自作CMS・自作サーバーにて運用中
ブログ・ホームページ >

Javascriptでスマホのスクリーンキーボードの高さとかを検出する話

公開日時: 2023-01-02 01:16:54更新日時: 2023-03-22 02:29:30

事の発端

「アプリが不便になった」知り合いから突然そんな一報があったのは大晦日の夜。
当該のwebアプリはたしかに当方が自作したものでしたが、2ヶ月以上更新していなかったはずがいきなり不具合の報告とあって、まさに寝耳に水でした。

調べてみると、どうやらChromeがバージョン108にアップデートされた辺りでAndroid版でのスクリーンキーボード周りの仕様に以下のような変更があった模様。
これまでのChromeスクリーンキーボードが表示されている間、webページはウインドウサイズがキーボードの高さの分だけ縮められたものとして扱われる
Chrome 108スクリーンキーボードはウインドウの上に重なって表示された扱いとなる。さらに、ページはフォーカスのあたっているインプットボックスの位置へ勝手にスクロールされる

ちなみに、Safariなどは以前から後者の仕様だったようですが、当方のwebアプリはAndroid向けだったためこのような仕様は考慮しておらず……
結果的に、正月早々からこの仕様変更への対処に追われることになってしまいました(まあ、これでiOS環境でも利便性が向上するというメリットはありましたが……)

追記: iOS対応を切り捨てるのであれば、「name="viewport"」のmetaタグに「interactive-widget=resizes-content」を追記すればChromeでは以前同様の動作となるようです。

スクリーンキーボードの情報を取得するAPIは存在しない

で、さっそくこの問題にJavascriptで対処することにしましたが、さっそく問題に直面しました。

結論から言ってしまいますが、2023年1月現在のJavascriptの標準仕様にはスクリーンキーボードの高さはおろか、スクリーンキーボードが表示されたことを検知するようなAPIすら存在していないのです。

これに対し、スクリーンキーボードの情報を取得する手段としては、以下のような手法が用いられているようでした。
  • インプットボックスのフォーカスイベントをキーボードの表示とみなす
  • ウインドウのリサイズをキーボードの表示とみなす
  • ウインドウのスクロール量を監視する
しかし、これらの手法は以下の理由から全て役に立ちません。

まず、インプットボックスのフォーカスイベントを使用する方法ですが、この方法ではキーボードの表示は検出できるものの、そのサイズを取得する方法がないため、キーボードの高さに応じてページの表示を調整するといった処理は不可能でした。

clientHeightなどを監視してウインドウのリサイズをキーボードの表示とみなす手法は前述した旧バージョンのChromeの仕様に依存したハックであり、今回問題となっているChrome 108には適用できませんでした

最後のスクロール量に関しても、スクリーンキーボードに隠れた範囲のスクロールはページのスクロールとはみなさないという謎の仕様があるらしく、window.scrollYを監視しても検出できないという結果に終わりました。

唯一の解決策「VisualViewport」

そんな状況でいろいろ検索して辿り着いた(今のところ)唯一の解決策が、window.visualViewportで取得できるオブジェクトを活用するものでした。

VisualViewportオブジェクトに関してはMDNのリファレンスも英語版しかないような状況ですが、ざっくりまとめると以下のようなことが可能です。
  • スクリーンキーボードを除いた表示範囲の幅や高さを取得する
  • スクリーンキーボードに隠れた範囲のスクロール量を取得する
  • スクリーンキーボードによる表示範囲の変化をイベントとして関数を実行する
  • スクリーンキーボードに隠れた範囲のスクロールをイベントとして関数を実行する
なお、VisualViewportオブジェクトのプロパティやイベントは全て読み取り専用であり、「スクリーンキーボードに隠れた範囲のスクロール位置を移動させる」や、イベントをキャンセルして「スクリーンキーボードに隠れた範囲のスクロールを無効化する」といった処理は不可能なようです。

スクリーンキーボードの表示を検出する

VisualViewportオブジェクトを利用してスクリーンキーボードの表示を検出し、その高さを推定するには、以下のようなアプローチとなります。
visualViewport.onresize = function () {
    if (vv.height < document.documentElement.clientHeight - 10) {
        // 表示時の処理
    } else {
        //非表示時の処理
    }
};
このイベントはキーボードの表示・非表示双方のタイミングで呼び出されることに留意する必要があります。
visualViewport.heightでスクリーンキーボードを除く表示範囲が取得できるため、この値をdocument.documentElement.clientHeightなどと比較し、キーボードの状態を推定しましょう。

なお、端末によってはvisualViewport.heightは整数にならないため、イコールによる比較ではなく、差が10px以下ならキーボード非表示とみなすといったような実装が現実的でしょうか。

onresizeは「visualViewport.addEventListener("resize", 関数名)」などの方法でも動作可能なようです。

キーボードに隠れた範囲のスクロールを検出する

VisualViewportオブジェクトを利用してスクリーンキーボードに隠れた範囲のスクロールを検出するには、以下のようなアプローチとなります。
visualViewport.onscroll=function(){
    console.log("スクロール量は" + visualViewport.offsetTop + "px");
};
なお、visualViewport.offsetTopもheight同様に整数とは限りません。

こちらについても「visualViewport.addEventListener("scroll", 関数名)」などの方法でも動作可能なようです。

デバッグについて

ちなみに、スクリーンキーボード表示時の動作のデバッグですが、現行のデスクトップ版Chromeの開発者ツールにはスクリーンキーボードの表示をエミュレートする機能はなく、デバッグ作業は全て実機か仮想マシンで行うしかないようです。

(2020年辺りまでは見かけ上ダミーのキーボードを表示する機能はあったらしいですが……)
この記事のタグ:
Javascript

この記事へのコメント