H5 软键盘兼容方案
在 App 内嵌 WebView 的聊天、评论、搜索等场景中,页面通常由三部分组成:顶部 Header(固定)、中间对话区(可滚动)、底部输入框(固定)。
理想状态下,软键盘弹出时只有中间对话区被压缩,Header 始终吸顶,输入框随键盘上推。但在 iOS 和 Android 上,实际情况往往与预期不符。本文整理了实际开发中常见的兼容问题,以及对应的处理方案。
一、理想布局
核心诉求可以概括为:键盘只影响中间内容区的高度,不改变 Header 和输入框的相对位置关系。
二、iOS
问题一:fixed 定位的 Header 跟着页面上滑
现象
输入框获得焦点后,Header 虽然设置了 position: fixed,但键盘弹出时仍会跟着整个页面一起滑走,无法保持吸顶。
原因
2015 年 3 月 iOS 8.2 发布后,键盘弹起时 WebView 不会被压缩,而是整体被顶上去。此时 fixed 元素不会重新计算定位,仍保持原来的相对位置,于是跟着页面一起上移。
解决方案
方案一:客户端原生 Header(推荐)
由客户端提供自定义 Header,或针对当前场景定制原生导航栏。原生 Header 不属于 WebView 内容,WebView 上推时它依然保持吸顶——这是从根源上规避问题的方式。
方案二:H5 动态修正 Header 定位
既然页面高度没变、只是整体上移,那么获取键盘高度后重新设置 Header 的 top 即可。
获取键盘高度有两种方式:
| 方式 | 原理 | 局限 |
|---|---|---|
window.pageYOffset |
页面上移的距离即键盘高度 | 通用性好 |
VisualViewport API |
用 WebView 高度减去可视区域高度 | iOS 13+ 才支持 |
推荐第一种方式。处理时机是输入框 focus 时——iOS 键盘弹出有约 300ms 延迟,需要 setTimeout 配合:
function isIOS() { |
iPad 横竖屏切换
iPad 翻转屏幕时,如果不重新计算 Header 定位,页面会出现错乱。监听 resize 让输入框失焦,聚焦时再重新计算:
window.addEventListener('resize', () => { |
已知缺点
切换键盘类型(如中英文切换)时,无法检测到键盘高度变化,页面会有轻微上下跳动。如需更平滑,可在 focus 期间监听页面滚动,实时更新 Header 的 top,blur 后恢复。
避坑:不要用 scrollTo 强拉页面
网上有一种做法是:键盘弹出后强制 scrollTo 把页面拉回来,再修改 body 高度:
let initialBodyHeight = 0; |
这种方式看似可行,但如果恰好在滑动页面时聚焦,scrollTo 会与 -webkit-overflow-scrolling: touch 冲突,导致滚动异常。处理滑动问题时,应尽量避开对该属性的影响——它能让滚动更流畅,建议保持开启:
-webkit-overflow-scrolling: touch; /* 手指离开后继续惯性滚动 */ |
方案三:H5 + 客户端 SDK
客户端提供 SDK,实时通知前端键盘高度变化,前端据此动态设置 top。精度和实时性最好,但需要客户端配合。
问题二:iOS 12.4.1 键盘收起后页面不恢复
现象
fixed 定位容器内的 input 聚焦时软键盘占位,失焦后键盘消失但占位未释放,导致输入框无法再次输入。
解决方案
失焦时主动滚动页面,释放占位:
window.scrollTo(0, 0); |
三、Android
Android 不会出现 Header 不固定的问题,但键盘弹出的表现分两种模式。
正常模式
键盘弹出时 WebView 被压缩,底部输入框自然上滑——最理想的状态,无需额外处理。
异常模式(华为等机型居多)
键盘弹出后页面毫无变化,WebView 没有被压缩,键盘直接覆盖在 H5 页面上,输入框被挡住。
原因尚不明确,且因为可视区没有变化,前端也无法直接获取键盘高度。
解决方案
方案一:H5 按屏幕比例估算键盘高度
思路比较粗糙:根据不同设备的宽高比推算键盘大概高度,给一个固定值。以下系数来自常见机型的实测经验,上线前需按目标设备微调。
- 监听
resize无效——异常机型可视区根本不改变 scrollIntoView也无效——系统认为屏幕大小就是可视区,不会产生滚动
聚焦后等待 600ms,对比前后可视区高度,过滤掉正常机型(正常机型键盘弹出后可视区会减小,不需要处理):
// 宽高比下限 → 键盘高度系数(值越小,估算键盘越高) |
失焦时重置页面高度即可。上面示例用 inputEl、inputBarEl 代指输入框及其容器,Vue 项目中可对应 $refs.input、$refs.inputBar。
缺点:实现有投机成分,不够严谨,但能兼容大多数异常机型。
方案二:H5 + 客户端 SDK
与 iOS 方案三相同,由客户端实时通知键盘高度,前端动态调整布局——最理想的长期方案。
四、方案对比
| 方案 | 适用平台 | 精度 | 客户端成本 | 推荐度 |
|---|---|---|---|---|
| 原生 Header | iOS | 完美 | 中 | ⭐⭐⭐⭐⭐ |
H5 修正 top |
iOS | 较好 | 无 | ⭐⭐⭐⭐ |
| H5 估算键盘高度 | Android 异常机型 | 一般 | 无 | ⭐⭐⭐ |
| 客户端 SDK | iOS / Android | 最好 | 高 | ⭐⭐⭐⭐⭐ |
五、总结
H5 软键盘兼容没有银弹,核心思路是:
- 能交给客户端的尽量交给客户端——原生 Header 或键盘高度 SDK 是最稳的方案
- iOS 的关键是补偿
pageYOffset——键盘顶起整个页面后,用top把 fixed 元素拉回来 - Android 异常机型只能曲线救国——按屏幕比例估算,先过滤正常机型再处理
- 避开
scrollTo+-webkit-overflow-scrolling: touch的组合——会引发滚动冲突
如果业务对体验要求高,建议推动客户端提供键盘高度 SDK,前端统一监听、统一适配,一劳永逸。
参考资料