Oteto Blogのロゴ

【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に対応していないので無理だった。