iOS: Виртуальная клавиатура и пустота. Часть первая

В одном из моих рабочих проектов есть модуль чата. Ничего сверхъестественного, классический набор из двух компонентов:

  1. Список сообщений (куда ж без него)
  2. Поле ввода (чтобы было где разгуляться)

Список занимает всю доступную высоту и скроллится. Поле ввода всегда зафиксировано внизу с помощью position: sticky;

Баг

Клиент обнаружил в этом модуле следующий баг:

  1. Тапнуть в поле ввода (сфокусироваться)
  2. Откроется виртуальная клавиатура iOS
  3. Проскроллить до конца страницы

Ожидание: дойдем до конца и либо упремся в него, как в стенку, либо словим «резиновый» эффект, который все так любят в iOS.

Результат: пустое пространство между клавиатурой и контентом:

Пустое место между клавиатурой и контентом iOS

Посмотреть пример (открывать, тапать и скроллить в Safari на iOS)

Данный баг возникает только в Safari и только при наличии открытой виртуальной клавиатуры.

Время костылей, господа!

Я потратил на анализ и исправление этого бага несколько рабочих дней и нашел аж два решения.

Первое — это просто шедевр инженерной мысли! Внимание: берем и просто сбрасываем фокус с поля, как только юзер начинает скроллить. Гениально же, а?

function resetFocus(): void {
  const input = document.querySelector("input");
  if (input) {
    input.blur();
  }
}

document.addEventListener("touchmove", () => resetFocus());

На самом деле, в какой-то момент я действительно думал, что этот ужасный костыль — единственный вариант, как, не дожидаясь фикса на стороне iOS, замаскировать проблему.

Решение со сбросом фокуса на touchmove

Спойлер: я ошибался, есть второе, хорошее решение, но оно требует определенной верстки и немного JS. Поэтому, чтобы не раздувать пост, я подробно расскажу об этом во второй части.

Но если совсем коротко: запрещаем скроллить body и переносим всю движуху внутрь дочернего элемента. Стили будут примерно такие:

html,
body {
  position: fixed;
  inset: 0 0 0 0;
  overflow: hidden;
  height: var(--window-inner-height);
  touch-action: none;
}

После этого, кроме бага, пропадет эффект «растягивания» на странице.

Из интересного

В примере я специально сделал theme_color и фон сайта разными, чтобы баг прямо бросался в глаза. Если цвет будет одинаковый, проблема будет не так заметна, но легче от этого никому не станет.

Баг при моно цвете

Занимательно, что этот баг оказался весьма распространенным явлением. После того как я перелопатил все свои стили, скрипты на проекте, мне пришла в голову идея проверить как обстоят дела с этим на других сайтах.

Из всех сайтов, на которых я проверял, только ВКонтакте справился на ура. Чего нельзя сказать про Ozon и Кинопоиск:

Ozon. Баг на iOS Кинопоиск. Баг на iOS

Источники

  1. https://ios-keyboard-bug.surge.sh/