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

Safari & Chrome Mobile Keyboard Fix

SOURCE HTML/JavaScript
VERSION 1.0
AUTHOR Cododel

A comprehensive fix for the notorious virtual keyboard bug that affects all web chats (Telegram Web, WhatsApp Web, etc.) in Safari, and viewport issues in Chrome Mobile. Inspired by Yandex Messenger’s UX solution.

The Problem

Safari Issue

When users type in a chat input on iOS Safari:

  1. Virtual keyboard opens
  2. User scrolls the page (not the chat)
  3. Input remains focused with keyboard open
  4. Page content becomes inaccessible behind keyboard
  5. No built-in way to dismiss keyboard

Chrome Mobile Issue

Virtual keyboard resizes viewport, breaking 100vh layouts in chat interfaces.

The Solution

Two-part fix:

  1. Safari: Detect page scroll while keyboard is open → auto-blur input
  2. Chrome: Use interactive-widget=resizes-content meta tag

Installation

1. Chrome/Android Fix (Meta Tag)

Add to your <head>:

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

2. Safari/iOS Fix (JavaScript)

Import and run globally in your main script:

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

Or include directly:

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

How Safari Fix Works

  1. Detection: Monitors visualViewport.resize events
  2. Keyboard State: Tracks if INPUT/TEXTAREA is focused
  3. Scroll Detection: Compares offsetTop changes (throttled)
  4. Auto-blur: Dismisses keyboard if page scrolls while keyboard is open

This mimics Yandex Messenger’s behavior - the only major chat app that solved this correctly.

Source Code

chrome-keyboard-fix.html (Meta tag)

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

safari-keyboard-viewport-fix.js (Complete solution)

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
},
}
}
})

Why This Matters

  • ✅ Fixes Telegram Web, WhatsApp Web, Discord Web in Safari
  • ✅ Prevents broken UX with stuck keyboards
  • ✅ Restores normal chat scrolling behavior
  • ✅ Zero dependencies, vanilla JavaScript
  • ✅ Battle-tested solution (4+ hours of research)

Affected Platforms

  • Safari iOS: ❌ Broken (fixed by this script)
  • Chrome Android: ❌ Viewport issues (fixed by meta tag)
  • Yandex Messenger: ✅ Already has this fix built-in

Technical Details

Research Time: ~4 hours
Inspiration: Yandex Messenger’s UX implementation
Complexity: Handles visualViewport API edge cases
Performance: Throttled to 100ms for smooth scrolling

[ ▲ 0 ]