移动端 fixed 元素被软键盘顶起是因浏览器将 fixed 锚定在视觉视口底部;应优先用 env(keyboard-inset-bottom)(iOS 16.4+),次选 visualViewport API 监听真实高度变化,fallback 到 focusin/blur + 延迟读取 innerHeight,并避免依赖 resize 或瞬时 innerHeight。

移动端 fixed 元素被软键盘顶起,不是 CSS 写错了,而是浏览器把 fixed 锚定在“视觉视口”底部——键盘一弹,视口变矮,它就自动上浮到新视口底边。纯靠 bottom: 0 或 height: 100vh 无法响应这种动态变化。
用 visualViewport 监听真实可视区域变化
这是目前最准、最轻量的方案,能直接拿到键盘弹出后用户真正看到的高度(visualViewport.height),不依赖抖动的 resize 或不可靠的 window.innerHeight。
- 页面加载时先存下无键盘状态的基准高度:
const baseHeight = visualViewport.height - 监听
visualViewport.addEventListener('resize', () => { ... }),计算差值:const kbHeight = baseHeight - visualViewport.height - 把这个差值用于调整按钮位置,比如:
btn.style.transform = `translateY(${kbHeight}px)`,比直接改bottom更少触发重排 - 注意:iOS Safari 15.4+、Chrome 61+ 支持;老版本需降级,不能只靠它
fallback 到 focusin/blur + 延迟高度探测
当 visualViewport 不可用(如 iOS 15.3 或某些安卓 WebView),必须退回到事件驱动方式,但不能在 focus 立即读 innerHeight——键盘还没完全弹出,高度没稳定。
- 监听
focusin(不是focus,因冒泡更可靠)到所有input和textarea - 用
setTimeout(..., 0)或requestAnimationFrame延迟读取window.innerHeight,确保拿到压缩后的值 - 对比初始高度,若下降 >150px,大概率是键盘弹出,此时将底部元素设为
position: absolute,并手动算bottom:${window.innerHeight - input.getBoundingClientRect().bottom}px -
blur后必须立刻恢复position: fixed; bottom: 0,否则滚动时定位彻底错乱
用 env(keyboard-inset-bottom) 让 CSS 自动适配(iOS 16.4+)
iOS Safari 16.4 起原生支持这个环境变量,键盘弹出时它会自动变成当前键盘高度,无需 JS 干预,是最干净的解法。
立即学习“前端免费学习笔记(深入)”;
- 直接写:
bottom: env(keyboard-inset-bottom, 0px),配合position: fixed - 必须搭配
<meta name="viewport" content="viewport-fit=cover">,否则不生效 - 安卓暂不支持,要用
@supports not (bottom: env(keyboard-inset-bottom))包裹降级样式 - 别和
env(safe-area-inset-bottom)混用——后者只管刘海/指示条,不管键盘
别碰 resize 事件,也别信 window.innerHeight 的瞬时值
这是最多人踩坑的地方:以为监听 window.addEventListener('resize', ...) 就能捕获键盘,结果 iOS 不触发,安卓触发两次,Chrome 还会先缩再回弹,造成按钮来回跳。
-
resize在键盘场景下行为极不稳定,2026 年仍不能作为主方案 -
window.innerHeight在键盘弹出瞬间可能短暂回弹(尤其安卓 Chrome),直接读它会得到错误高度 - 不要用
scrollIntoView({ block: 'end' })强推按钮到底部——键盘已弹出时它可能白屏或滚动失控 - 慎用全局
scroll-behavior: smooth,部分安卓机型会卡死
真正难的不是写几行代码,而是判断该用哪一层策略:现代 iOS 优先 env(keyboard-inset-bottom),新浏览器走 visualViewport,老环境才切到 focus/blur + setTimeout。混用时记得加清理逻辑,比如 blur 后没及时恢复样式,下次滚动就会暴露问题。


















