最稳纯CSS方案是min-height:100dvh+flex布局推底,用margin-top:auto或align-items:flex-end定位按钮;需配viewport-fit=cover、@supports env(keyboard-inset-bottom)及position:sticky备用。

纯 CSS 无法可靠避免底部按钮被顶起,但可以大幅降低出错概率——关键不是“修 fixed”,而是绕开它。
用 min-height: 100dvh + flex 布局推到底部
这是目前最稳、副作用最少的纯 CSS 方案,不依赖视口锚点,自然避开键盘压缩 visualViewport 的问题。
- 根容器(如
body或一个全屏div)必须设display: flex; flex-direction: column; min-height: 100dvh - 内容区用
flex: 1占满剩余空间,底部按钮作为兄弟节点,不设position: fixed - 底部按钮靠
margin-top: auto或父容器设align-items: flex-end推到底部,位置完全由布局流决定 - 不要用
100vh:iOS Safari 中它包含地址栏高度,100dvh才是动态可视高度;不支持 dvh 的老浏览器需 JS fallback
用 @supports + env(keyboard-inset-bottom) 做 iOS 原生避让
仅限 iOS Safari 16.4+,但效果最干净,零 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 会跳过甚至解析失败 - 安卓完全不支持该变量,别把它当通用解法——它只是 iOS 的“加速键”
用 position: sticky 替代 fixed(推荐首选)
兼容性已足够好(iOS 15.4+、Android Chrome 90+),且比 absolute 更少踩坑。
立即学习“前端免费学习笔记(深入)”;
- 父容器需设
min-height: 100vh,且不能有overflow: hidden或任何transform(会破坏 sticky 触发) - 底部元素写
position: sticky; bottom: 0;,它仍在文档流中,不受 visualViewport 缩放影响 - 如果父容器高度塌陷(比如只写了
height: 100%而没设min-height),sticky 会直接失效 - 老版本可渐进降级为
position: absolute+ JS 动态计算,但 sticky 本身已覆盖绝大多数真实用户
为什么 resize 和 @media 都靠不住
它们根本捕获不到键盘弹出的真实状态。媒体查询不响应键盘,resize 在 iOS 上触发延迟、抖动、甚至漏掉第一次弹出。
-
window.addEventListener('resize')不是键盘事件监听器,只是视口尺寸变化的副产品,不可信 -
@media (max-height: ...)依赖初始设备参数,键盘弹出不会触发重匹配 - 真正可靠的信号是
visualViewport.height变化,或focusin/blur+setTimeout(() => {}, 0)等布局完成后再读取位置 - 所有纯 CSS 方案都只是“缓解”,复杂场景(如多输入框共用底部栏、横屏缩放、微信 WebView)仍需 JS 动态兜底


















