position: sticky 是替代 fixed 的首选方案,需父容器 min-height: 100vh 且无 transform/overflow:hidden/fixed 父级,底部元素设 sticky + bottom: 0 + width: 100%。

别硬刚 position: fixed —— 它在软键盘弹出时必然错位,这不是你写错了,是浏览器行为本身就不支持动态锚定。 所有纯 CSS 的 bottom: 0、100vh、z-index 都无法修复,必须换锚点或绕开视口依赖。
用 position: sticky 替代 fixed(推荐首选)
这是目前副作用最小、零 JS、兼容性足够落地的方案,iOS 15.4+ 和 Android Chrome 90+ 均支持。
- 父容器(如
#app或main)必须设min-height: 100vh,不能只写height: 100vh,否则键盘弹出时容器塌陷 - 父容器不能有
transform、overflow: hidden或position: fixed父级,否则sticky会静默失效(无报错) - 底部元素直接写
position: sticky; bottom: 0;,并加width: 100%防横屏偏移 - 不要和
env(keyboard-inset-bottom)混用——sticky本身已响应键盘高度变化,叠加反而可能冲突
监听 focusin 动态切 absolute(兼容性最广)
当需支持 iOS 15.3 或老安卓 WebView 时,这是唯一可靠路径。但只改 position 属性必穿帮,必须同步约束容器和计算逻辑。
- 根容器(如
body或#app)设position: relative; min-height: 100vh; - 底部元素初始为
position: fixed; bottom: 0;,focusin触发后立刻切为position: absolute; bottom: 0; -
focusin回调里必须加setTimeout(() => { ... }, 0),否则getBoundingClientRect()读的是键盘弹出前的旧坐标 - 计算
bottom值用window.innerHeight - inputRect.bottom,不是写死数值;blur后立刻恢复fixed并清除top/bottom行内样式 - 务必检查
document.activeElement === inputEl,防止异步焦点转移导致状态错乱
用 env(keyboard-inset-bottom) 做 iOS 原生避让(仅限 Safari 16.4+)
这是最干净的纯 CSS 方案,但仅对 iOS 有效,安卓完全不识别,且必须严格满足前提条件才能生效。
-
<meta>必须含viewport-fit=cover,否则env()恒为0px - 写法只能是
bottom: env(keyboard-inset-bottom, 0px),漏掉 fallback 会导致键盘收起后按钮悬空 - 整条规则必须包裹在
@supports (bottom: env(keyboard-inset-bottom))中,否则旧版 Safari 会忽略甚至解析失败 - 别和
svh混用:bottom: calc(0px + 100svh)是语义错误——svh是高度单位,env(keyboard-inset-bottom)是偏移量
给内容容器加 padding-bottom(兜底最稳)
这是兼容性最广、最不容易翻车的方式,适用于所有安卓 WebView 和老 iOS,但必须作用在正确层级。
- 把
padding-bottom加在最外层内容容器(如#app),不是body—— 后者易触发外边距合并 - 值要匹配底部按钮实际高度(含
border和padding,不含margin),例如按钮高 60px 就写padding-bottom: 60px - 必须声明
box-sizing: border-box,否则 padding 会让容器额外变高 - 若需兼容 iPhone 底部安全区,再补一行
padding-bottom: env(safe-area-inset-bottom),同样需viewport-fit=cover
最容易被忽略的是:所有方案都依赖容器结构稳定。如果页面用了 transform、overflow: hidden 或动态 height 计算,getBoundingClientRect() 和 scrollIntoView() 都可能失效,且不报错。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











