iOS safariの悪しきタブバー(ナビゲーションバー)によるズレをjsで補正する方法


結論

js側でコントロールする時は、

window.addEventListener('resize', callback);

を使うしかないっぽいです。
説明不要だと思いますが、スクロールに伴うタブバーの表示/非表示に伴ってwindowのサイズが変化するたびに、その都度callbackで設定した関数を実行してくれます。
※後述しますが、iOS13の場合callback関数内で値を計算しようとすると正しい値が取れないことがあります。

背景

レスポンシブなサイトを作成していて、ある要素の位置を取得し、その位置に合わせてjs側でモジュールのコントロールをしたい時がありました。
例としては、あるモジュールの上に重なるコンテンツが出た際、それに合わせて下のモジュールの位置を自動調整したい。(要するに、人の目で目視できるように位置を移動したい)みたいな状況ですね。

基本的にはjs側で、cssのtopの値を

document.querySelector('.Banner').getBoundingClientRect().top

みたいな感じでとってきて、それをoffset値としてコントロールする方針になると思いますが、safariの場合はこのtopの値が、下に表示されるタブバーのせいでずれてしまいます。
(特に、DOM読み込み時などにwindow.innerHeight - top値 みたいな計算をやってoffsetを設定すると、思ったような挙動になりません。)

基本的な施策としてはCSS側で-webkit-fill-availableを使う方法が主張されるのですが、chromeでは安定しないなどの理由で、いろいろな側面から見てもjs側でやっておいた方が安定しています。

かなりシンプルですが、特にiOS safariでのtop値の挙動が特殊で少し嵌っていたのでメモです。

補足(iOS13の場合)

iOS14だと発生せずiOS13だと発生する事象として、callback関数で以下のような計算をする場合、resizeイベントの発生タイミングとinnerHeight値の反映が同期していないため計算値がおかしくなってしまいます。

callback = function () {
    val = window.innerHeight - document.querySelector('hoge').getBoundingClientRect().top
}

あまり良い解決策ではありませんが、恣意的にsetTimeoutで少しタイミングを送らせてあげることで解決します。