必须组合CSS环境变量、布局重构和JS事件监听三类手段:iOS用env(keyboard-inset-bottom)配合@supports和viewport-fit=cover;安卓等场景改用position:sticky或flex布局;JS需监听focusin/blur并用visualViewport动态调整。

不能只靠 bottom: 0 + position: fixed,这是浏览器机制决定的失效,不是写法错误。必须组合 CSS 环境变量、布局重构和 JS 事件监听三类手段,缺一不可。
用 env(keyboard-inset-bottom) 做 iOS 原生避让(16.4+)
iOS Safari 16.4 起支持该环境变量,键盘弹出时自动注入当前键盘高度,是目前最干净的纯 CSS 解法:
- 必须在
中启用适配: - 写法必须带 fallback:
bottom: env(keyboard-inset-bottom, 0px),不能省略, 0px - 必须用
@supports包裹,否则旧版 Safari 会忽略整条规则:@supports (bottom: env(keyboard-inset-bottom)) { .btn-footer { bottom: env(keyboard-inset-bottom, 0px); } } - 安卓 WebView 完全不支持,不能单独依赖它;也别和
svh混用,比如bottom: calc(100svh - 100px)是无效逻辑
改用 position: sticky 替代 fixed(推荐首选)
如果你的底部按钮本就紧贴内容流末端(如聊天输入框下方、评论提交区),sticky 是副作用最少、兼容性最好的方案:
- 设
position: sticky; bottom: 0;,父容器需有明确高度约束,例如min-height: 100vh - 它不脱离文档流,不会被键盘“悬空”顶起,也不受
transform触发的层叠上下文干扰 - 注意:父容器必须允许内部滚动(如设置了
overflow-y: auto),否则 sticky 不会生效 - 不要在 flex 容器里对子元素设
align-self: flex-end来模拟 sticky——那只是视觉错觉,仍可能被顶起
用 flex 布局把“固定”效果做进文档流
彻底绕开视口锚定问题,让底部元素随容器自然排布:
- 根容器(如
#app)设display: flex; flex-direction: column; min-height: 100dvh;(优先用dvh,fallback 可加 JS 判断) - 主内容区设
flex: 1;或margin-top: auto;,底部栏作为兄弟节点普通块级元素 - 避免直接在
body上设height: 100vh——iOS 地址栏会干扰,100dvh更准,但老浏览器需降级为min-height: 100vh+ JS 补偿 - 底部栏不能是
position: fixed,否则仍会被顶;也不能套在 flex 子项内部再设align-self,必须是同级兄弟节点
JS 动态 fallback:监听 focusin/blur + visualViewport
当以上 CSS 方案都不可用(如老安卓 WebView),必须用 JS 主动响应:
- 监听
focusin(不是focus),目标为所有input、textarea和[contenteditable] - 用
visualViewport?.height获取真实可视高度(比window.innerHeight准确得多),iOS 15.4+/Chrome 61+ 支持 - 若
visualViewport不可用,focusin后用setTimeout(() => { ... }, 0)延迟读取input.getBoundingClientRect().bottom,再算bottom = window.innerHeight - inputRect.bottom -
blur回调里必须立刻恢复原始定位,否则滚动时元素会消失或错位;多个输入框共用同一底部栏时,需全局记录document.activeElement
真正难的不是选哪个方案,而是意识到:没有单点解法。iOS 16.4+ 可以靠 env(),但安卓必须降级;sticky 最轻量,但只适用于内容流末端;flex 最稳,但要求结构改造。最容易被忽略的是 blur 后的恢复时机——延迟哪怕一帧,滚动时按钮就会彻底失联。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











