env(keyboard-inset-bottom)是iOS Safari 16.4+最干净的键盘避让方案,需viewport-fit=cover声明并配合@supports降级;安卓及旧iOS必须用JS监听focusin/blur动态调整bottom,且须延迟读取、阈值判断、清除内联样式。

直接用 env(keyboard-inset-bottom) 是目前最干净的解法,但仅限 iOS Safari 16.4+;安卓完全不支持,必须搭配 JS fallback 才能落地。
为什么 position: fixed 一定会被顶起
不是你 CSS 写错了,而是浏览器把 fixed 锚定在「视觉视口(visual viewport)」底部,软键盘弹出时这个视口高度被压缩(Android)或冻结上推(iOS),但元素定位值不会重算。结果就是按钮飘在键盘上方、卡在半空,甚至被顶出可视区。bottom: 0、100vh、@media 都无效,因为这些值不随键盘动态更新。
用 env(keyboard-inset-bottom) 做 iOS 原生避让
这是零 JS、最轻量的方案,但有硬性前提:
- 必须在
中启用适配: - 写法必须带 fallback:
bottom: env(keyboard-inset-bottom, 0px),不能只写env(keyboard-inset-bottom),否则键盘收起后按钮悬空 - 必须用
@supports降级,否则旧版 Safari(如 15.3)会忽略整条规则甚至解析失败:@supports (bottom: env(keyboard-inset-bottom)) { .btn-footer { bottom: env(keyboard-inset-bottom, 0px); } } - 别和
svh或safe-area-inset-bottom混用——前者是高度单位,后者管刘海,都不管键盘
安卓和老 iOS 必须用 JS fallback
纯 CSS 无法感知键盘状态,安卓完全不支持 env(keyboard-inset-bottom),iOS 15.x 及更早版本也不识别。可靠入口只有 focusin 和 blur:
- 监听所有可聚焦元素:
input、textarea、[contenteditable],漏掉textarea就等于漏掉一半场景 -
focusin回调里必须加setTimeout(() => {}, 0),否则getBoundingClientRect()返回的是键盘弹出前的旧坐标 - 计算
bottom值用window.innerHeight - inputRect.bottom,不是写死数值 -
blur后立刻恢复position: fixed和原始bottom: 0,并调用el.style.removeProperty('top')和el.style.removeProperty('bottom')清除内联样式 - 别依赖
window.addEventListener('resize'):iOS Safari 15.4+ 触发率低于 20%,安卓延迟严重,且常只在键盘收起时触发一次
最容易被忽略的底层细节
iOS 键盘收起后 window.innerHeight 不立即恢复、focusin 触发早于键盘完全展开、Safari 对 window.scrollTo() 有节流策略——这意味着延迟读取、阈值判断(比如下降 >150px 才认为是键盘)、手动锚点恢复不是可选项,是必做动作。哪怕用了 visualViewport API 或 env(keyboard-inset-bottom),也得为老版本兜底,且不能跳过状态清理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











