优先用env(keyboard-inset-bottom)实现ios原生避让,需viewport-fit=cover且fallback为0px;次选position:sticky替代fixed;兜底用focusin/blur动态切换absolute定位并精确计算bottom值。

移动端底部固定导航栏被软键盘顶起,不是定位写错了,而是 position: fixed 锚定的是「视觉视口」底部,而键盘弹出会压缩或上推这个视口——但元素不重算位置,结果就飘在半空。纯靠 bottom: 0 或 100vh 无效,必须分层应对。
优先用 env(keyboard-inset-bottom) 做 iOS 原生避让(16.4+)
这是目前最干净的纯 CSS 方案,键盘弹出时自动上浮,收起后回退到 0px,零 JS 干预。
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">必须存在,否则变量不触发 - 样式必须写成
bottom: env(keyboard-inset-bottom, 0px)——0px是 fallback,漏掉会导致键盘收起后按钮悬空 - 必须用
@supports (bottom: env(keyboard-inset-bottom))包裹,否则旧版 Safari 会整条规则忽略甚至解析失败 - 安卓完全不支持该变量,别把它当通用解法;也别和
svh混用,比如bottom: calc(0px + 100svh)是错的
推荐首选 position: sticky 替代 fixed
如果你的导航栏本就紧贴内容流末端(如聊天输入框上方、评论区底部),sticky 是副作用最少、兼容性已足够好的选择。
- 父容器需设
min-height: 100vh,且不能加overflow: hidden或transform,否则sticky失效 - 导航栏本身写
position: sticky; bottom: 0;,它不脱离文档流,天然不受视觉视口压缩影响 - iOS 15.4+、Android Chrome 90+ 均支持;老版本可渐进降级为
absolute+ JS 计算 - 比
absolute更少踩坑:不用管setTimeout时机、不依赖focusin精确坐标、也不怕父容器transform干扰
兜底用 focusin/blur 动态切 absolute 定位
这是覆盖机型最广的 JS 方案,但关键不在换定位,而在锚点控制和时机处理。
- 根容器(如
#app或body)必须设position: relative和min-height: 100vh(不能只写height: 100%,否则键盘弹出时塌陷) - 监听所有可聚焦元素:
input、textarea、[contenteditable];用focusin(不是focus),因冒泡更可靠 -
focusin后必须用setTimeout(() => {}, 0)等布局更新完成,再读input.getBoundingClientRect().bottom - 计算
bottom值用window.innerHeight - inputRect.bottom,不是top,也不是写死数值 -
blur后立刻恢复position: fixed; bottom: 0,并用el.style.removeProperty('bottom')清除内联样式,否则滚动时消失
真正难的不是写哪一行 CSS,而是判断当前环境是否支持 env(keyboard-inset-bottom)、是否能用 sticky、还是必须上 JS;以及在 focusin 的瞬间,如何确保拿到的是键盘弹出后的稳定高度——很多项目卡在这一步,不是逻辑错,是 setTimeout 没等够,或者没清掉旧的 bottom 内联样式。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











