0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【第039回】スマホ最大の強敵!iOS/Androidのソフトウェアキーボード出現に伴うビューポート崩壊との死闘

0
Posted at

こんにちは!2ちゃんねる互換掲示板システム「 つゔぁいちゃんねる(2ch.biz) 」の開発録、第39回です。

Webアプリケーションを開発していて、全フロントエンドエンジニアが一度は頭を抱え、枕を濡らす最大の魔境――。
それこそが 「スマートフォンのソフトウェアキーボード出現に伴うビューポート崩壊」 です。

デスクトップのブラウザでは完璧に動いていたP2Pチャット画面を、iPhoneやAndroidの実機で開いた瞬間、私たちは目を疑う光景に直面しました。

「画面を開いた時点で、アドレスバーのせいで最下部のメッセージ入力欄が半分見切れている」
「入力欄をタップした瞬間、キーボードが下から競り上がってきて、 入力欄がキーボードの裏側に完全に隠れてしまう! 」
「画面が勝手にズームして、右側の参加者リストがどこかへ吹き飛んだ!」

本稿では、iOS Safari と Android Chrome という2大巨頭の変態的挙動をねじ伏せ、ネイティブチャットアプリと寸分違わぬ「キーボードに吸い付く入力バー」を実現した死闘の全記録を解説します!


1. モバイルWebを襲う「4大ビューポート地獄」

ネイティブアプリ(SwiftやKotlin)であれば、OSがキーボードの開閉に合わせてレイアウトを自動調整してくれます。しかし、ブラウザ上のWebアプリでは、以下の4つの罠が同時に襲いかかってきます。

特に最悪だったのが 「② 入力欄がキーボードの裏に隠れる現象」 です。自分が今何をタイピングしているのかが一切見えず、変換候補すら確認できない「盲目タイピング」を強いられていたのです。


2. 解決へのアプローチ:最新Web標準とAPIの総動員

この複雑怪奇な問題を解決するため、私たちは単一のハックに頼るのではなく、4つの最新技術を組み合わせて防壁を築きました。


3. 実装の全貌

(1) Viewportメタタグの進化:interactive-widget の指定

モダンブラウザ(Chrome 108+ 等)でサポートされた最新の属性 interactive-widget=resizes-content を Viewport メタタグに追加しました。

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content, viewport-fit=cover">
  • interactive-widget=resizes-content: キーボードが出現した際、ブラウザのオーバーレイではなく「コンテンツのリサイズ」として扱わせる指示。
  • viewport-fit=cover: iPhoneのノッチ(切り欠き)やセーフエリアの全画面描画を許可。

(2) 100vh の追放と 100dvh への移行

古典的な height: 100vh; は、アドレスバーが出ている状態でも「アドレスバーが引っ込んだときの最大高さ」を返してしまうため、最下部が見切れる最大の原因でした。

これを、アドレスバーの伸縮にリアルタイム連動する 100dvh(Dynamic Viewport Height) および JavaScriptが計算するカスタムプロパティへと置き換えました。

html, body {
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden; /* 画面全体のバウンススクロールを防止 */
}

.p2p-standalone-app {
    display: flex;
    flex-direction: column;
    width: 100%;
    /* 動的ビューポート高さ(フォールバック付き) */
    height: 100dvh;
    height: var(--p2p-viewport-height, 100dvh);
    overflow: hidden;
}

(3) window.visualViewport API によるリアルタイム追従

iOS Safari など、CSSだけでは完全に追従しきれない環境を完全に制覇するため、VisualViewport API を用いてミリ秒単位で高さを同期します。

/**
 * 仮想キーボード連動&ビューポート監視ハンドラ
 */
function setupViewportHandler() {
    if (!window.visualViewport) return;

    function onViewportChange() {
        const height = window.visualViewport.height;
        // キーボードの高さを差し引いた可視領域のピクセル数をCSS変数に注入
        document.documentElement.style.setProperty('--p2p-viewport-height', `${height}px`);

        // 入力欄にフォーカスがある場合、最下部へ自動スクロール
        const activeEl = document.activeElement;
        if (activeEl && (activeEl.id === 'p2p-message-input' || activeEl.tagName === 'TEXTAREA')) {
            setTimeout(() => {
                activeEl.scrollIntoView({ block: 'nearest' });
                if (typeof scrollToBottom === 'function') {
                    scrollToBottom();
                }
            }, 50);
        }
    }

    // キーボードの出現・格納、画面回転を検知
    window.visualViewport.addEventListener('resize', onViewportChange);
    window.visualViewport.addEventListener('scroll', onViewportChange);

    // 初期化時にも一度実行
    onViewportChange();
}

(4) iOSの自動ズーム防止とセーフエリア対応

/* iOSのフォーカス時勝手ズームを防止(font-size 16px 以上が必須) */
@media (max-width: 768px) {
    .p2p-xchat-text-input,
    #p2p-message-input {
        font-size: 16px !important;
    }

    /* iPhone ホームバー(下部インジケーター)との重なり防止 */
    .p2p-xchat-input-row {
        padding-bottom: max(8px, env(safe-area-inset-bottom));
        padding-left: max(8px, env(safe-area-inset-left));
        padding-right: max(8px, env(safe-area-inset-right));
    }
}

[!IMPORTANT]
iOS Safari は、入力フォームの font-size が 16px 未満 (例えば 14px や 15px)の場合、「文字が小さくて読みにくいだろう」と判断して画面全体を自動で拡大ズームしてしまいます。一度ズームされるとユーザーがピンチインで戻すまでレイアウトが崩壊するため、 モバイルでの font-size: 16px 指定は絶対厳守の鉄則 です。


4. 自動テストと実機での動作検証

専用のテストスクリプトおよび iPhone(iOS 17 Safari)と Android(Pixel / Chrome)の実機で検証を行いました。

テスト項目 従来の挙動 今回の改修後
初期表示時の入力バー アドレスバーに押されて画面下に沈没 画面最下部にピタッと収まる
キーボード出現時 入力欄が背後に隠れて見えなくなる キーボードの上端にピタッと吸い付く!
文字入力中の画面ズーム 勝手にズームして左右がハミ出る 倍率 1.0 倍を完全にキープ
iPhone ホームバー操作 送信ボタンとホームバーが被って誤爆 セーフエリア分の余白で快適操作

5. まとめと次回予告

モバイルWebにおける最大の難所「ソフトウェアキーボード問題」を、interactive-widget、100dvh、VisualViewport API、そして 16pxルール の連携プレーによって完全制覇しました。
これにより、スマートフォンからでもLINEやDiscordと全く遜色のない、極上のタイピング体験が手に入りました。

しかし、キーボードに追従するようになったことで、今度は別の問題が露呈しました。

「キーボードが開いたとき、画面全体の高さが縮むのはいいんだけど、 チャット部屋のタイトルやホスト名が書かれた上部ヘッダーまで一緒にスクロールして画面外へ逃げてしまう! 」
「今どの部屋で話しているのか分からなくなるし、退出ボタンが押せない!」

次回は、 【第040回】キーボードが出てもヘッダーが逃げない!固定ヘッダーと可変スクロール領域の完全分離 をお届けします。お楽しみに!


本番サービス : https://2ch.biz/

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?