【JavaScript】iOSでposition:fixedな要素をキーボード上に押し上げる

やりたいこと

モバイルだとテキストボックスなどにfocusするとバーチャルキーボードが表示されるが、iOSの場合position:fixedで配置した要素はこれによって隠れてしまうことがある。

YouTubeネイティブアプリのコメント欄

そこで上記のYouTubeネイティブアプリのように、position:fixedな要素をキーボードの上に添えながら押し上げて表示させたい。

解決法

CSSのみだと厳しいが、JavaScriptのvisualViewportAPIを利用することでお手軽に実現できる。

<body>
<input type="text" />
<button>sample button</button>
</body>
button {
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
background-color: black;
color: #fff;
text-align: center;
line-height: 50px;
font-weight: bold;
transition: bottom 0.3s;
}

まずサンプルのレイアウトを作成する。

function registerPushupEvent() {
if (!/iPhone|iPad|iPod/.test(navigator.userAgent)) return;
const button = document.querySelector("button");
visualViewport.addEventListener("resize", ({ target }) => {
const keyboardHeight = window.innerHeight - target.height;
const bottomValue = keyboardHeight === 0 ? "" : `${keyboardHeight}px`;
button.style.bottom = bottomValue;
});
}
registerPushupEvent();

visualViewportの基本的な使い方について、詳しくは下記記事で触れている。

【JavaScript】visualViewportでスマホのバーチャルキーボードの出現を検知し高さを取得する

キーボードに押し上げられる形で対象要素が動いてくれた

これで無事、キーボードに押し上げられる形で対象要素が動いてくれた。(なぜかXcodeのシミュレーター上ではresizeイベントの発火までにラグがあったが、実機だと想定通りに動作した)

その他に試したこと

キーボードの高さの決め打ち

調べたところどうやらシェア率の高いiPhoneだと仮想キーボードの高さが270px辺りらしいので、キーボードの高さを決め打ちして動かすという方法でもできなくはない。

function registerPushupEvent() {
if (!/iPhone|iPad|iPod/.test(navigator.userAgent)) return;
const input = document.querySelector("input");
input.addEventListener("focus", () => (button.style.bottom = "270px"));
input.addEventListener("blur", () => (button.style.bottom = ""));
}
registerPushupEvent();

しかし全デバイスでキーボードの高さが同一ではないので、配置がズレてしまうことを容認できる場合のみにしか使えない。ゆえにこの方法は断念した。

VirtualKeyboard APIの利用

VirtualKeyboard APIはその名の通り、ブラウザの仮想キーボードを制御するためのもの。

navigator.virtualKeyboard.addEventListener("geometrychange", ({ target }) => {
const { height } = target.boundingRect;
});

上記のように書くことでキーボードの高さを取得できるが、iOS16時点ではそもそもこのAPIがSafariに対応していないので無理だった。