Safari & Chrome Mobile Keyboard Fix | Cododel
CODODELDEV
EN / RU
Back to Deck
[fix]

Safari & Chrome Mobile Keyboard Fix

ИСХОДНИК HTML/JavaScript
ВЕРСИЯ 1.0
АВТОР Cododel

Комплексный фикс для печально известного бага виртуальной клавиатуры, который затрагивает все веб-чаты (Telegram Web, WhatsApp Web и др.) в Safari, и проблемы viewport в Chrome Mobile. Вдохновлен UX решением Яндекс Мессенджера.

Проблема

Проблема Safari

Когда пользователи печатают в input чата на iOS Safari:

  1. Открывается виртуальная клавиатура
  2. Пользователь скроллит страницу (не чат)
  3. Input остается в фокусе с открытой клавиатурой
  4. Контент страницы становится недоступным за клавиатурой
  5. Нет встроенного способа закрыть клавиатуру

Проблема Chrome Mobile

Виртуальная клавиатура изменяет размер viewport, ломая 100vh layouts в интерфейсах чатов.

Решение

Двухчастный фикс:

  1. Safari: Определяет скролл страницы при открытой клавиатуре → авто-снятие фокуса
  2. Chrome: Использует meta тег interactive-widget=resizes-content

Установка

1. Фикс Chrome/Android (Meta тег)

Добавить в <head>:

<meta
name="viewport"
content="width=device-width,initial-scale=1.0,user-scalable=no interactive-widget=resizes-content"
/>

2. Фикс Safari/iOS (JavaScript)

Импортировать и запустить глобально в главном скрипте:

import './safari-keyboard-viewport-fix.js'

Или подключить напрямую:

<script src="safari-keyboard-viewport-fix.js"></script>

Как работает фикс Safari

  1. Обнаружение: Мониторит события visualViewport.resize
  2. Состояние клавиатуры: Отслеживает фокус на INPUT/TEXTAREA
  3. Определение скролла: Сравнивает изменения offsetTop (с throttling)
  4. Авто-blur: Закрывает клавиатуру если страница скроллится при открытой клавиатуре

Это имитирует поведение Яндекс Мессенджера - единственного крупного чат-приложения, которое решило это корректно.

Исходный код

chrome-keyboard-fix.html (Meta тег)

<meta
name="viewport"
content="width=device-width,initial-scale=1.0,user-scalable=no interactive-widget=resizes-content"
/>

safari-keyboard-viewport-fix.js (Полное решение)

document.addEventListener('DOMContentLoaded', () => {
if (isSafari() && window.visualViewport) {
let { getValue: getOffsetTop, setValue: setOffsetTop } = useThrottledValue(
window.visualViewport.offsetTop,
100,
)
let keyboardIsOpen = false
const hideKeyboardOnScrollWindow = (e) => {
const windowIsScrolled = window.visualViewport.offsetTop !== getOffsetTop()
if (keyboardIsOpen && windowIsScrolled) {
document.activeElement.blur()
}
}
window.visualViewport.addEventListener('resize', (e) => {
const isInputFocused = ['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName)
if (isInputFocused && !keyboardIsOpen) {
keyboardIsOpen = true
window.addEventListener('touchmove', hideKeyboardOnScrollWindow)
window.addEventListener('scroll', hideKeyboardOnScrollWindow)
}
if (!isInputFocused) {
keyboardIsOpen = false
window.removeEventListener('touchmove', hideKeyboardOnScrollWindow)
window.removeEventListener('scroll', hideKeyboardOnScrollWindow)
}
setOffsetTop(window.visualViewport.offsetTop)
})
}
function isSafari() {
const userAgent = navigator.userAgent
return userAgent.includes('Safari') && !userAgent.includes('Chrome')
}
function useThrottledValue(initialValue, delay) {
let rawValue = initialValue
let throttledValue = initialValue
let timeoutId = null
const updateThrottledValue = () => {
throttledValue = rawValue
}
return {
setValue: function (newValue) {
rawValue = newValue
if (!timeoutId) {
updateThrottledValue()
timeoutId = setTimeout(() => {
timeoutId = null
}, delay)
}
},
getValue: function () {
return throttledValue
},
}
}
})

Почему это важно

  • ✅ Исправляет Telegram Web, WhatsApp Web, Discord Web в Safari
  • ✅ Предотвращает сломанный UX с застрявшими клавиатурами
  • ✅ Восстанавливает нормальное поведение скролла чата
  • ✅ Без зависимостей, ванильный JavaScript
  • ✅ Проверенное решение (4+ часа исследований)

Затронутые платформы

  • Safari iOS: ❌ Сломано (исправлено этим скриптом)
  • Chrome Android: ❌ Проблемы viewport (исправлены meta тегом)
  • Яндекс Мессенджер: ✅ Уже имеет встроенный фикс

Технические детали

Время исследования: ~4 часа
Вдохновение: UX реализация Яндекс Мессенджера
Сложность: Обрабатывает edge cases visualViewport API
Производительность: Throttling до 100ms для плавного скролла

[ ▲ 0 ]