position: fixed 在移动端因锚定视觉视口(visual viewport)导致软键盘弹出时定位错位;env(safe-area-inset-bottom) 需 viewport-fit=cover 才生效;env(keyboard-inset-bottom) 仅 iOS Safari 16.4+ 支持;padding-bottom 是跨平台稳定解,但需 JS 动态修正。

不是 CSS 写错了,而是 position: fixed 在移动端被浏览器按「视觉视口(visual viewport)」锚定,而软键盘弹出时这个视口高度会压缩,但 fixed 元素仍按原始高度计算位置——结果就卡在键盘底下。
fixed 锚定的是 visual viewport,不是 layout viewport
Android WebView 里 window.innerHeight 会随键盘弹出明显变小(比如从 600px 缩到 400px),iOS Safari 却基本不变;但无论哪边,position: fixed 都只认当前 visual viewport 的 bottom 边界。键盘一出来,这个边界上移了,fixed 按照旧基准定位,自然就“浮”在键盘上方空白处,而不是贴着键盘顶。
- iOS:常冻结 layout viewport 高度,但把页面往上推,fixed 元素跟着上移,却没留出键盘空间
- Android:直接缩
window.innerHeight,fixed 仍用缩前的尺寸算 bottom,导致错位更隐蔽 - 微信 WebView、企业微信等 Hybrid 容器行为更碎片化,有的甚至不触发 resize 事件
env(safe-area-inset-bottom) 返回 0px 的真正原因
env(safe-area-inset-bottom) 不是失效,是压根没启用——iOS Safari 默认 viewport-fit=auto,此时所有 safe-area-inset-* 变量恒为 0px。
- 必须在
<head>中显式声明:<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> - 漏掉
viewport-fit=cover,哪怕 CSS 写得再对,env(safe-area-inset-bottom)也永远返回0px - 部分构建工具(如 Vite 插件)会自动注入
meta标签,但可能覆盖或忽略viewport-fit,务必检查最终 HTML 源码
bottom: env(keyboard-inset-bottom) 在安卓上完全无效
env(keyboard-inset-bottom) 是 iOS Safari 16.4+ 独占特性,安卓系浏览器(Chrome、WebView、QQ 浏览器等)根本不识别该变量,写上去等于白写。
立即学习“前端免费学习笔记(深入)”;
- 直接写
bottom: env(keyboard-inset-bottom, 0px)而不做@supports降级,安卓会忽略整条规则,按钮 fallback 到bottom: 0,照样被遮 - 不能和
svh混用:bottom: calc(0px + 100svh)语义错误,svh是高度单位,env(keyboard-inset-bottom)是偏移量 - 旧版 Safari(iOS 15.3 及以下)也不支持,必须搭配
visualViewport监听或focusin+ 延迟读取window.innerHeight降级
padding-bottom 是唯一跨平台稳态解
给内容容器(如 #app 或 <main>)加 padding-bottom,本质是把内容“顶上来”,预留物理空间——它不依赖视口变化,不触发重排,兼容所有浏览器。
- 别作用于
body或html:会导致整个页面下移,顶部状态栏区域也可能错位 - 必须配
box-sizing: border-box,否则padding会让容器总高度超出预期 - 值要匹配按钮真实高度(含内边距、border),例如
padding-bottom: 60px - 需叠加安全区时,双保险写法:
padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);,且constant()必须在前
最易被忽略的一点:软键盘弹出后,env(safe-area-inset-bottom) 和 env(keyboard-inset-bottom) 都不会动态更新,但视口高度变了——这意味着纯 CSS 预留的 padding-bottom 会突然过大,内容被顶到屏幕中间。这不是你漏写了什么,是浏览器机制限制,必须靠 JS 动态响应 visualViewport.resize 或输入框焦点事件来修正。


















