这篇专门记一个移动端的经典坑:软键盘。做过 H5 聊天界面的应该都被它折磨过,我接 AI 对话框的时候又栽了一次。
现象
页面布局很常规:上面一个滚动的消息列表,底部一个 fixed 的输入框。PC 上完美。一到手机上,点输入框,软键盘弹起来------
- iOS Safari 上,底部 fixed 的输入框直接被顶到屏幕中间,飘着,下面一大片空白还能看到背景。
- 安卓部分机型上,输入框倒是没飘,但消息列表没跟着往上推,最新消息被键盘盖住了,用户看不到 AI 刚回的话。
体验稀碎。
原因
简单说,iOS 的 position: fixed 在键盘弹起时不靠谱,它不会随着可视区域收缩重新定位,而是相对一个"假的"视口。安卓各家 webview 对键盘弹起是 resize 还是 overlay 又各有脾气,行为不统一。
我最后的解法
折腾了好几版,记两个关键点。
第一,别用 fixed 钉底,改用 flex 撑满。 外层容器 height: 100% + display: flex; flex-direction: column,消息列表 flex: 1; overflow-y: auto,输入框自然排在最下面。这样键盘把容器高度压缩时,列表区域跟着缩,输入框始终贴在缩小后的容器底部,不飘。
第二,配合 visualViewport 监听键盘。 iOS 上拿 window.visualViewport.height 才是键盘弹起后真实可视高度,用它去校正容器高度:
javascript
const vv = window.visualViewport
vv?.addEventListener('resize', () => {
document.documentElement.style.setProperty(
'--app-h', `${vv.height}px`
)
})
然后容器用 height: var(--app-h, 100vh)。
第三,键盘弹起后把列表滚到底 ,否则最新消息还是可能被盖。监听到 resize 就 scrollTop = scrollHeight。
还没解决干净的
visualViewport 在一些老安卓 webview 里没有,我只能降级回 window.innerHeight 的兜底,那些机器上偶尔还是会抖一下。这块没洁癖地全解掉,先这样了。
对话本身的模型我接的讯飞 MaaS,现成 API,重心全放在端上这些破事上了。移动端聊天界面你们还踩过啥键盘坑?评论区交流。