Этот пост о том времени, когда я понял, как сильно я ненавижу фронтенд-разработку для iOS. Если фон вас не интересует, просто прокрутите вниз до решения.

Около 1,5 лет назад мы переделывали функцию чата в Career Accelerator, и я выбрал задачу изменить вход. Это должна была быть небольшая задача: изменить кнопку отправки и размер ввода, также при отправке ввод должен выдвигаться, а затем снова вдвигаться для другого вопроса, и мы готовы к работе. Через 1 час работы все соответствовало дизайну, и на Android вел себя вполне прилично. Затем я протестировал его на iOS… и вот тут-то все и пошло не так. Ввод невозможно было контролировать, он прыгал в верхнюю часть экрана, когда клавиатура была открыта, и исчезал куда-то в невидимку, когда клавиатура была закрыта. Иногда сообщения в чате тоже не были видны. Чат на iOS нельзя было использовать без ибупрофена. В свое оправдание должен сказать, что стало не намного хуже, чем до моих изменений.

Поиск помощи в Интернете не увенчался успехом. Все, что я смог найти, — это сообщения о переполнении стека без ответов и псевдорешения, которые не работали. Прочитав некоторые статьи, я понял, что проблема связана с окном просмотра. Когда вы открываете клавиатуру на Android, размер области просмотра изменяется по вертикали, чтобы компенсировать видимую клавиатуру. iOS не изменяет размер области просмотра при открытии клавиатуры, то есть клавиатура вместо этого блокирует часть области просмотра, которая в противном случае была бы видимой. Поэтому, когда кто-то пытается исправить что-то внизу экрана, оно на самом деле находится внизу экрана, но находится за клавиатурой и не видно. Таким образом, position: fixed; не всегда работает должным образом на iOS.

Конечно, я не мог уложиться в срок, потому что не ожидал, что этот глупый ввод займет так много времени. Тем не менее, я все еще не был готов сдаться, потому что в то время я думал, что это безумие, что мы предоставляем инструмент на основе ИИ, но не можем контролировать вход в чат. В поисках решения я зарегистрировался на каждой доступной мне веб-платформе, на которой была похожая функция чата. Пробовал Tinder, Instagram, Telegram, разных ботов и чаты поддержки в интернет-магазинах. Я подключил MacBook к своему телефону и все отладил. Удивительно, но у всех чатов была одна и та же проблема — вход, который вы не могли контролировать, и это очень раздражало. Вы когда-нибудь замечали, насколько плохи веб-чаты на iOS? Для меня это было большим осознанием того, что еще никто не смог исправить вход внизу!

В конце концов я нашел одно приложение, которое было почти идеальным. Это была российская социальная сеть «ВКонтакте». Они использовали какой-то фреймворк, и на каждом шагу было так много прослушивателей событий и манипуляций с JavaScript, что было невозможно отследить, как они творили чудеса. Попытки найти их разработчика и попросить решения не увенчались успехом.

К тому времени я уже провел 1,5 отчаянных недели, пытаясь найти все, что только можно было найти, чтобы заставить его работать. В конце концов я пришел к решению с одним приемом, благодаря которому все выглядит не так уж и плохо.

Вот пример кода решения:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
      .container {
        display: flex;
        flex-direction: column;
        height: 100%;
        width: 100%;
        }
      .messages-container {
        /*reserve 50px for input*/
        height: calc(100% - 50px);
        display: flex;
        flex-direction: column;
        width: 100%;
        overflow-x: hidden;
        overflow-y: auto;
        flex-flow: column nowrap;
      }
      .fixed-container {
        display: flex;
        flex-direction: column;
        justify-content: flex-end;
        height: 50px;
        position: fixed;
        bottom: 0;
        right: 0;
        width: 100%;
        margin-top: 0;
        z-index: 100;
      }
      .relative-container {
        position: relative;
        opacity: 1;
      }
      form {
        background-color: white;
      }
      input {
        background-color: white;
      }
    </style>
  
  </head>
<body>
  <div class="container">
    <div class="messages-container">
        Here we display all messages from users and from the bot
    </div>
    <div class="fixed-container">
        <div class="relative-container">
            <form action="">
                <input type="text"/>
            </form>
        </div>
    </div>
  </div>
</body>
</html>
  • Сделать основной гибкий контейнер, чтобы поместить все внутрь (.container)
  • Сделайте два контейнера на странице. Первый будет содержать сообщения (.messages-container), а второй будет зарезервирован для самого ввода (.fixed-container).
  • Внутри .fixed-container поместите .relative-container. Это облегчает управление предметами внутри.
  • Создайте форму с вводом внутри.

Приведенный выше код исправляет скачки и исчезновение ввода, но ввод по-прежнему не выглядит фиксированным внизу и не очень стабилен. Вот тут-то и появляется хитрость. Нам нужно немного JavaScript, чтобы сначала определить, что пользователь использует iOS, а затем, чтобы прослушивать события focus и focusout.

const getMobileOperatingSystem = () => {
        const userAgent = navigator.userAgent || navigator.vendor || window.opera;
        // Windows Phone must come first because its UA also contains "Android"
        if (/windows phone/i.test(userAgent)) {
            return 'windows';
        } else if (/android/i.test(userAgent)) {
            return 'android';
        } else if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
            return 'ios';
        } else {
            return 'unknown';
        }
    };
      document.addEventListener('DOMContentLoaded', () => {
        'use strict';
        const input = document.querySelector('input');
        const inputColor = 'white';
        const backgroundColor = 'black';
    
        if (input) {
          if (getMobileOperatingSystem() === 'ios') {
          input.addEventListener('focus', () => {
            document.body.style.backgroundColor = inputColor;
          }, false);
    
          input.addEventListener('focusout', () => {
             document.body.style.backgroundColor = backgroundColor;
         }, false);
       }
      }
    });

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

Так вот! Если вы хотите иметь красивый чат, который ведет себя так, как вы ожидаете, на всех системах, все же лучше сделать нативное приложение. Но, если вы все еще хотите иметь его в Интернете, этот трюк очень легко сделать, и он выглядит и ведет себя хорошо в 90% случаев.

Вот — это сам чат, если хотите попробовать.