Книжная полка

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/