How to Keep Fixed Headers and Footers Stable When the iOS Keyboard Pops Up
This article explains why fixed elements break when the iOS soft keyboard appears in a WebView, compares iOS and Android keyboard behaviors, shows how to listen for keyboard events, and provides practical solutions—including native headers, scrollIntoView tricks and scroll adjustments—to keep sticky UI components correctly positioned.
Problem
When an input field gains focus on iOS, the soft keyboard pushes the page up, causing fixed elements to lose their position; the sticky header is pushed out of view and the sticky footer is covered by the keyboard.
UI Desired Optimizations
Keep the sticky header fixed.
Keep the sticky footer (button) above the keyboard.
Maintain a 48px gap between the input field and the keyboard after it appears.
Key Questions to Understand
1. What happens when the keyboard appears?
iOS and Android behave differently. On iOS the WebView does not resize; instead the whole page scrolls upward by the keyboard height. On Android the WebView height shrinks by the keyboard height, and the page itself cannot scroll.
2. Why does fixed fail?
Because iOS does not expose the keyboard size to the page, the WebView scrolls up without recalculating fixed positions, so they move with the scroll. After the keyboard hides, the page “bounces back” but fixed elements are not recomputed correctly.
3. How to listen for keyboard show/hide?
iOS: listen to focus (show) and blur (hide) events on the input. Android: listen to the resize event and compare the WebView height.
// iOS keyboard show/hide
inputRef?.current?.addEventListener('focus', function() {
// iOS keyboard shown
}, false);
inputRef?.current?.addEventListener('blur', () => {
// iOS keyboard hidden
}); useEffect(() => {
const { isAndroid } = Util.getOS('');
let originHeight = document.documentElement.clientHeight || document.body.clientHeight;
const handleResize = throttle(() => {
const newHeight = document.documentElement.clientHeight || document.body.clientHeight;
if (originHeight < newHeight) {
// Android keyboard hidden
} else {
// Android keyboard shown
}
originHeight = newHeight;
}, 300);
if (isAndroid) {
window.addEventListener('resize', handleResize, false);
}
return () => {
if (isAndroid) {
window.removeEventListener('resize', handleResize, false);
}
};
}, []);Solutions
1. Keep the sticky header
Because the WebView cannot keep a CSS fixed header on iOS, we use the native client header (JSB) which stays fixed outside the WebView. This required redesigning the header to a simple back button and centered title.
Derived Issue on Android
The native header reduces the WebView height, causing the bottom button to be hidden when the keyboard appears.
Fix
Add a bottom margin equal to the header height to the sticky footer when the keyboard is shown.
2. Keep the sticky footer above the keyboard
On iOS the maximum scroll distance equals the keyboard height. Calling inputElement.scrollIntoView(true) forces the page to scroll exactly that distance, moving the footer above the keyboard.
3. Ensure the input stays visible
Simply call scrollIntoView(true) on the input element when the keyboard appears.
4. Fix iOS keyboard hide “gray area” bug
On some iOS WeChat WebViews (iOS 12, Xcode 10 builds) the page does not scroll back down after the keyboard hides. The workaround is to force a scroll to the top or bottom.
Scroll to top window.scrollTo(0, 0); Scroll to bottom
window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));Specific handling for WeChat 6.7.4+ on iOS 12:
const handleIosInputBlur = () => {
const wechatInfoRe = /MicroMessenger\/([\d\.]+)/i;
const wechatInfo = wechatInfoRe.exec(window?.navigator?.userAgent);
const wechatVersion = wechatInfo && wechatInfo.length > 1 && wechatInfo[1];
const osInfoRe = /OS (\d+)_(\d+)_?(\d+)?/i;
const osInfo = osInfoRe.exec(navigator.appVersion);
const osVersion = osInfo && osInfo.length > 1 && osInfo[1];
if (!wechatVersion || !osVersion) return;
const height = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
if (Number(wechatVersion.replace(/\./g, '')) >= 674 && Number(osVersion) >= 12) {
window.scrollTo(0, height);
}
};Final Result
iOS
Android
References
WebView soft‑keyboard compatibility solutions
How to get iOS keyboard height with JavaScript
Common mobile input issues and fixes
iOS keyboard and visualViewport API
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
