はじめに

前回、iOS 26 の iPhone で、Web 頁内の入力欄にフォーカスすると、固定しているメニューバーがスクロールしてしまうという問題に直面した件について記述しました。

結果、一通りの原因調査に納得し、次の記事の投稿に向けて作業をすすめていたのですが、再び新たなる問題に直面してしまいました。

実は、iOS 26の viewport 問題ですが、<input> タグだけではなかったのです。その問題は、普段、何気なく行う動作の中に潜んでいました。

今回は、その件についての調査と考察を記述したいと思います。


問題の詳細

発生している問題は、当サイトを iPhone で表示した状態で、画面を縦向き → 横向き → 縦向きと回転すると、top:0 に固定化しているはずのメニューバーが半分ほど上部にスクロールしてしまうというものです。

問題発生後はリロードしても状況が改善されず、上方向スクロールでは、メニューヘッダーが必ず半分ほど、上部からはみ出し、スクロールされてしまうようになります。

しかし、メニューバーの固定化が完全に外れているわけではなく、下方向のスクロールでは、問題なく、top:0 の位置に固定化されます。

最初、当サイトの作りの問題も疑ったのですが、他のサイトでも同じように問題が発生するので、原因は別にありそうです。

また、サイトによっては、その問題を織り込み済みなのか、画面ずれを考慮しているようにも見えるデザインのサイトもありました。

※ iOS26.6.2 では、状態が改善しています。問題自体は発生するのですが、縦画面に戻った後、スクロールして、メニューバーを表示すると、それ以降、top:0 の位置に固定化されます。


問題発生時の実際の画面の確認

では、実際にどのような問題なのか、再現手順に従い、画面をみていただきたいと思います。

※ 画面左上には、デバッグ情報として下記のデータを表示しています。

  • event: デバッグ情報を記録した際のイベント
  • scrollY: window.scrollY の値
  • innerHeight: window.innerHeight の値
  • vv.height: window.visualViewport.height の値
  • innerWidth: window.innerWidth の値
  • vv.width: window.visualViewport.width の値
  • vv.offsetTop: window.visualViewport.offsetTop の値
  • vv.offsetLeft: window.visualViewport.offsetLeft の値
  • vv.scale: window.visualViewport.scale の値
  • clientHeight: document.documentElement.clientHeight の値
  • bcr.height: document.documentElement.getBoundingClientRect().height の値
  • clientWidth: document.documentElement.clientWidth の値
  • type: screen.orientation.type の値
  • header.bcr.top: ヘッダーの getBoundingClientRect().top の値
  • header.bcr.bottom: ヘッダーの getBoundingClientRect().bottom の値

※ vv: window.visualViewport

※ bcr: BoundingClientRect()

1. 画面の初期表示

画面の初期表示です。上部メニューバーも固定されており、問題ありません。

(図1)画面の初期表示

■ デバッグ情報

  • event: vv resize
  • scrollY: 0
  • innerHeight: 553
  • vv.height: 553
  • innerWidth: 375
  • vv.width: 375
  • vv.offsetTop: 0
  • vv.offsetLeft: 0
  • vv.scale: 1
  • clientHeight: 553
  • bcr.height: 7940
  • clientWidth: 375
  • type: portrait-primary
  • header.bcr.top: 0
  • header.bcr.bottom: 40

※ vv: window.visualViewport

※ bcr: BoundingClientRect()

2. 縦表示から横表示への回転

画面を縦表示から横表示に回転した直後の画面です。ここでも特に問題ありません。

(図2)縦表示から横表示への回転

■ デバッグ情報

  • event: orientationchange
  • scrollY: 0
  • innerHeight: 319
  • vv.height: 319
  • innerWidth: 667
  • vv.width: 667
  • vv.offsetTop: 0
  • vv.offsetLeft: 0
  • vv.scale: 1
  • clientHeight: 319
  • bcr.height: 10870.5
  • clientWidth: 667
  • type: landscape-primary
  • header.bcr.top: 0
  • header.bcr.bottom: 43

※ vv: window.visualViewport

※ bcr: BoundingClientRect()

3. 横表示から縦表示への回転

再び回転し、画面を横表示から縦表示に戻した直後の画面です。この状態で、メニューバーが半分ほど上にかくれてしまっています。

(図3)横表示から縦表示への回転

■ デバッグ情報

  • event: orientationchange
  • scrollY: 0
  • innerHeight: 573
  • vv.height: 573
  • innerWidth: 375
  • vv.width: 375
  • vv.offsetTop: 0
  • vv.offsetLeft: 0
  • vv.scale: 1
  • clientHeight: 573
  • bcr.height: 7940
  • clientWidth: 375
  • type: portrait-primary
  • header.bcr.top: 0
  • header.bcr.bottom: 40

※ vv: window.visualViewport

※ bcr: BoundingClientRect()

4. ヘッダー表示まで下スクロールする

ヘッダーが表示される正しい位置まで下スクロールした後の画面です。メニューバーは、top:0 の位置に留まります。

(図4)ヘッダー表示まで下スクロールする

■ デバッグ情報

  • event: vv resize
  • scrollY: 0
  • innerHeight: 553
  • vv.height: 553
  • innerWidth: 375
  • vv.width: 375
  • vv.offsetTop: 0
  • vv.offsetLeft: 0
  • vv.scale: 1
  • clientHeight: 573
  • bcr.height: 7940
  • clientWidth: 375
  • type: portrait-primary
  • header.bcr.top: 0
  • header.bcr.bottom: 40

※ vv: window.visualViewport

※ bcr: BoundingClientRect()

一見、もとの状態に戻ったように見えますが、これ以降、上方向スクロールを行うと、メニューバーが半分ほど上にかくれてしまう状態が継続して発生します。


原因と思われること

状態 innerHeight visualViewport.height documentElement.clientHeight ヘッダーtop
初期表示 553 553 553 0
縦→横回転 319 319 319 0
横→縦回転 573 573 573 0
下スクロール 553 553 573 0

横→縦回転後にヘッダーの上部が半分ほど隠れてしまう現象には、viewport 関連の値が正常な状態に戻らず、不整合が残っていることが関係している可能性があります。

特に、横→縦回転直後には、window.innerHeight、window.visualViewport.height、document.documentElement.clientHeight のすべてが初期状態より 20px ほど大きくなっています。

その後、スクロールを行うと、window.innerHeight と window.visualViewport.height は初期状態の値に戻りますが、document.documentElement.clientHeight は573pxのまま残ります。

header.getBoundingClientRect().top は、この間も常に 0 を示しています。つまり、JavaScript から取得できるヘッダーの表示位置は、常にビューポート上端の 0px です。

それにもかかわらず、実際の画面ではヘッダーの一部が上部に隠れて表示されます。

このことから、今回の現象は単純に sticky や fixed の指定が解除されているというより、画面回転後の viewport 関連の状態と、実際の画面表示との間に何らかの不整合が発生している可能性があると考えています。

ただし、今回の調査では、viewport の値が 20px 大きくなったことと、ヘッダーが隠れて表示されることとの直接的な因果関係までは確認できませんでした。


対策と復旧方法

残念ながら、この問題に対しても、いろいろ試してみたのですが、前回の問題と同様、当サイトではHTML・CSS・JavaScriptによる確実な回避策を見つけることができませんでした。

また、問題が発生した後は、ページをリロードしても、iPhone をスリープさせた後、再びロックを解除しても状態は復旧しませんでした。

現在確認できている復旧方法は、一度タブを閉じ、新しいタブで当サイトを開き直すしかありません。

こうすることで、問題が発生していた状態から正常な状態に戻ることを確認しています。


お願い

現時点では当サイト側で確実な回避方法を確認できていないため、iPhone をご利用の方には、この点をご承知の上でご利用いただきますようお願いいたします。

もしこの現象が発生した場合は、「対策と復旧方法」に記載した方法で復旧することができます。