Safari だけじゃなくてすべてのブラウザで入力時ビューポートがズレる (ので直しましょう)
なぜズレるのか
Safari の有名なバグ? というか仕様で、position: fixed;
や position: sticky;
で要素を固定しているとき、仮想キーボードを出すと正しい位置に表示されないというものがある。
iOS Safariはスクリーンキーボードを開いているとposition: fixedがスクロールに付いてこなくなる – 橋本商会
これは、キーボードが出ると、特定の条件を満たすときにビューポートがキーボードの高さ分ズレるからで、Safari 独自の仕様だった。理想的な挙動ではないとはいえ、固定されている要素が縦に長い場合にスクロールできる範囲が極端に小さくなってしまうことを防ぐための独自の仕様といえる。
がしかし、Chrome 108 でなぜか Android Chrome までもがこの独自の仕様に追従してしまった。ただし Chrome では meta
要素 (viewport
の interactive-widget
キー) で挙動の変更を追加でサポートしており、本来の (正しいと思われる) 挙動に戻すことが可能になっている。
Android Firefox はこの挙動をサポートしていないブラウザであったが、Firefox 131 から、Safari デフォルトの挙動に追従し、Firefox 132 で interactive-widget
による挙動の変更をサポートする。
もはや、これは Safari のバグなどではなく、全ブラウザ共通の仕様に変わってしまった。
修正方法
先ほど紹介したように、meta viewport
で interactive-widget
キーに resizes-content
という値を追加すると、今までの Firefox や かつての Chrome と同じ挙動に戻る。
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">
なお、Safari は対応しておらず、Safari で対応しようとすれば、JavaScript を書く必要がある。
技術的な解説
ブラウザで見える範囲という意味で「ビューポート」という名前がついているが、これに加えて「ビジュアルビューポート (視覚的ビューポート)」というものがある。これは、実際に表示されている範囲で、例えばピンチ操作で拡大しているときやキーボードが表示されているときに本来のビューポートよりも小さくなる。
モバイル用ブラウザでは、キーボードが表示される領域に被るテキスト入力欄をタップしたときなど、特定の状況において、ビューポートをキーボードの高さ分上にズラす、という挙動をする。これが発動すると、画面上部に固定されている要素などが画面外に移動してしまい、おかしな挙動になってしまう。
interactive-widget
キーは「resizes-visual
」「resizes-content
」「overlays-content
」の 3 つの値のどれかを取る。それぞれの値の意味はこのようになっている。
resizes-visual
: interactive な widget が表示されるとき、ビジュアルビューポートのみ大きさを変更する。resizes-content
: interactive な widget が表示されるとき、ビジュアルビューポートに加え「ビューポート」も大きさを変更する。overlays-content
: interactive な widget が表示されるとき、ビジュアルビューポートの大きさを変えない。
ここでいう interactive な widget というのは、画面に表示されるスクリーンキーボードのことである (今後増えるかもしれない)。画面のズームなどでのビジュアルビューポートの変更時は、ビューポートに影響しない。
つまり、resizes-content
を指定すると、ビューポートがズレようとするとき、上下ビューポートもリサイズされるため、正しい位置に固定した要素が表示される。ただし、キーボードが画面に出ているので、固定される要素が大きすぎてスクロールの領域が圧迫されないよう気をつける必要がある。
Safari は知らんw (多分、キーボードが出てるときだけ window.scrollY
とかをゴニョゴニョすれば良いはず)