软键盘弹起导致页面错位的根本原因是平台softinputMode默认行为不一致,需通过pages.json设adjustResize、监听uni.onKeyboardHeightChange及平台差异化适配三层解决。
软键盘弹起导致页面整体上移或底部元素被顶飞,根本不是样式写错了,而是平台层面对 softinputmode 的默认行为不一致。app 端默认用 adjustpan,整个 webview 被推上去;h5 没统一标准,ios 和 android 浏览器响应方式还不同;小程序虽有自动滚动,但定位常不准。直接改 css 或加 position: fixed 基本无效,得从配置 + 监听 + 平台适配三层下手。
pages.json 里必须设 softinputMode: "adjustResize"
这是最基础、最有效的起点,只对 App 平台(iOS/Android)生效:
-
adjustPan(默认)会让整个页面像纸片一样被键盘“托”起来,fixed 元素错位、导航栏被顶、输入框被遮是常态 -
adjustResize会压缩 Webview 高度,触发重排,布局更可控,也更接近 Web 开发直觉 - 必须写在对应页面的
pages.json的app-plus配置下,重启项目才生效 - 顺手加上
"softinputNavBar": "none"可隐藏 iOS 键盘上方横条,减少干扰
uni.onKeyboardHeightChange 是跨端监听的唯一可靠方式
resize、focus、blur 这些 DOM 事件在 H5 和小程序里不可靠,尤其收起后页面不复原的问题,全靠它:
- 它在键盘高度变化时触发(弹出、收起、横竖屏切换都算),返回
{ height: number } - H5 下需配合
uni.getSystemInfo获取原始windowHeight,对比判断是否处于“被压缩”状态 - 不要在回调里直接改
data或调pageScrollTo,要加throttle(比如 100ms 节流),否则频繁触发导致卡顿或定位抖动 - 示例逻辑:键盘弹出时记录当前滚动位置,收起后
setTimeout(() => pageScrollTo(...), 50)补偿回滚
fixed 元素被顶飞?别信 position: fixed 在移动端的稳定性
在 App 端 adjustPan 模式下,fixed 元素实际是被系统强制“粘”在 Webview 底部,而 Webview 自己上移了——所以它看起来飘到了屏幕中间。解决思路不是修 CSS,而是绕开:
- 把底部按钮等关键元素从
fixed改成absolute+ 底部容器padding-bottom动态预留键盘高度 - 或者用 flex 布局:父容器
display: flex; flex-direction: column;,中间内容区flex: 1,底部区域固定高度,不受键盘影响 - H5 场景下,可监听
uni.onWindowResize,当res.size.windowHeight 就认为键盘弹出了,临时隐藏底部 tabbar - nvue 页面需用
bindingx或原生模块,uni.onKeyboardHeightChange不可用
键盘收起后页面卡在高位?多数是视口未还原导致的假象
这不是 bug,是很多手机浏览器(尤其安卓 WebView)在键盘收起后没主动触发 resize,导致 window.innerHeight 还停留在压缩后的值。用户手动滚动一下就恢复,说明 DOM 没错乱,只是视口没刷新:
- 不能依赖
blur后立刻scrollTo(0, 0),iOS 上会闪一下,Android 上可能无效 - 稳妥做法:在
uni.onKeyboardHeightChange回调中,当height === 0时,用setTimeout(() => { uni.pageScrollTo({ scrollTop: lastScrollTop }); }, 150) - 如果用了
vh单位(如height: 100vh),务必换掉——它在键盘弹出时取的是压缩后视口高度,收起后不会自动更新 - 真机测试时重点看 Android 低版本(如 8.0)、iOS 15+、微信内置浏览器这三类,它们行为差异最大
真正难的不是某一行代码,而是同一套逻辑要在 App、H5、小程序三个环境里分别验证行为是否收敛。尤其是 adjustResize 在 H5 下完全不生效,而 uni.onKeyboardHeightChange 在 H5 下又比小程序延迟高。别指望一次配置通吃,得接受“分而治之”这个事实。











