はじめに
チャットUIや入力フォームで、画面下に常時表示する送信ボタン付きの入力バーを position: fixed で作った。PCではどのタイミングでも下端に張り付いて見える。でもiPhoneで入力欄をタップしてキーボードを開き、その状態のままページをスクロールすると、入力バーが本来の位置から浮いたようにズレて表示されたり、画面の途中で止まったりする。
これも実装ミスではなく、iOS Safariがキーボード表示中のスクロールでposition: fixed要素をどう扱うかに起因する問題です。原因と、固定配置に依存しない対策をまとめます。
何が起きているか
iOS Safariはソフトキーボードが開いたとき、ページ全体のサイズ(レイアウトビューポート)はそのままで、実際に見えている範囲(ビジュアルビューポート)だけがキーボードの高さ分縮みます。キーボードを開いただけで操作しなければ、fixed要素は見た目上はキーボードの直上に正しく表示されます。
問題はその状態でページをスクロールしたときに起きます。本来position: fixedはスクロールしても画面内の同じ位置に留まり続けるはずの仕組みですが、キーボード表示中はビジュアルビューポートとレイアウトビューポートの基準がずれているため、スクロール中に固定要素の位置計算が一時的に狂い、入力バーが本来の位置から浮いたように見えたり、スクロールを止めた場所によって表示位置がずれたりします。
.input-bar {
position: fixed;
bottom: 0; /* キーボード表示中にページをスクロールすると位置がズレる */
}
なぜPCのブラウザで気づかないのか
PCにはソフトキーボードがありません。物理キーボードで入力してもビューポートのサイズは変わらないため、position: fixed は常に意図した通りに動きます。Chrome DevToolsのモバイルシミュレーターも、キーボード表示によるビューポートの縮小までは再現しません。
この問題は実機のiPhoneで、かつキーボードを開いた状態でページをスクロールしないと確認できません。キーボードを開くだけのテストでは再現せず見落とされがちです。チャットの履歴を遡ったり長いフォームをスクロールしたりする画面ほど、実際にユーザーがこの操作をする場面が多く影響が大きくなります。
この挙動は記事内では再現できません
キーボード表示中のスクロールによるズレは、記事内の小さい枠では再現できません。お手元のiPhoneで以下のリンクを開いて確認してください。
各ページの下にある入力欄をタップしてキーボードを表示し、その状態のままページを上下にスクロールしてください。「崩れるパターン」は入力バーが本来の位置から浮いたようにズレて表示されます。
修正方法:fixedをやめてflexレイアウトに変更する
画面全体を display: flex; flex-direction: column のコンテナにし、本文を flex: 1; overflow-y: auto のスクロール領域、入力バーをその下に続く通常のflex子要素として配置します。固定配置をやめ、レイアウトの一部として扱うのがポイントです。
.screen {
height: 100dvh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.content {
flex: 1;
overflow-y: auto; /* 本文だけがスクロールする */
}
.input-bar {
flex-shrink: 0;
position: sticky; /* fixedではなくstickyに留める */
bottom: 0;
}
この構成なら、入力バーは本文のスクロールコンテナに対してstickyで留められた、ページ全体とは独立した要素になります。fixedのようにレイアウトビューポート全体を基準にした位置計算をしないため、キーボード表示中にスクロールしても位置がずれません。
position: stickyを使う理由
入力バーには position: fixed ではなく position: sticky を使います。sticky はページ全体ではなく、その要素が属するスクロールコンテナを基準に位置を決めます。スクロールしてもコンテナ内での相対位置がずれないため、fixedで起きていたスクロール中の位置のブレが発生しません。
fixedのまま直す方法としてwindow.visualViewportのresizeイベントでキーボードの高さを取得し、JSで都度位置を補正するやり方もありますが、スクロール中のブレまで完全に抑えるのは難しく実装も複雑になります。レイアウトをflex化してstickyに任せる方が、対応コードが少なく安定します。
まとめ
iPhone Safariはキーボード表示時、ページ全体のサイズ(レイアウトビューポート)を変えずに見えている範囲(ビジュアルビューポート)だけを縮めます。キーボードを開いただけならposition: fixed要素は正しい位置に見えますが、その状態でページをスクロールすると位置計算がずれ、入力バーが浮いたようにズレて表示されます。
対策は、固定配置に依存せずflexで画面を本文とフッターに分け、本文側だけをスクロールさせ、フッターにはposition: stickyを使うことです。チャット入力欄や検索バーなど、キーボードを開いたままスクロールする場面が多い画面を作るときは、固定フッターを使う前に一度実機のiPhoneで「開いたままスクロール」を試しておくと安心です。
関連するUI事例
キーボードを使う画面でこの問題が起きやすい代表的なUI事例です。