应使用 min-height: 100dvh + max-height: 100dvh + overflow-y: auto 替代 100vh,配合 viewport-fit=cover,并在老版本中监听 focus/blur 动态切换样式。

别指望 100vh 或 position: fixed 自己扛住键盘——它们在软键盘弹出时根本不会重算,遮挡是必然的,不是你写错了。
为什么 100vh 在键盘弹出时会失效
浏览器把软键盘当成“视口的一部分”临时重算 vh,但 iOS Safari 和多数安卓 WebView 并不更新 window.innerHeight,也不触发 resize 事件;结果就是 height: 100vh 的容器还撑着原始高度,底部 input 直接被截断。
- 改用
min-height: 100dvh+max-height: 100dvh+overflow-y: auto,dvh始终代表无键盘时的完整视口高(Chrome 105+、Safari 16.4+ 支持) - 必须配
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">,否则dvh退化为不可靠行为 - 老版本兼容:监听
focusin/blur,动态切换 class,层叠min-height: 100vh和height: 100dvh
scrollIntoView 对 fixed 元素无效怎么办
因为 position: fixed 脱离文档流,scrollIntoView 找不到滚动上下文,尤其在 iOS 上常只滚到键盘边缘,而非输入框顶部。
- 别对
fixed元素直接调element.scrollIntoView() - 监听
focus,加setTimeout(() => ..., 80)等 DOM 重排完成后再滚动,避免坐标取旧值 - 更稳做法:给
input父容器设scroll-margin-bottom: 120px,再调element.scrollIntoView({ block: 'nearest', behavior: 'smooth' }) - 微信安卓版不支持
scroll-margin-bottom,需降级为window.scrollTo(0, inputEl.getBoundingClientRect().top - 100)
如何让底部按钮不被键盘盖住
用 position: sticky 替代 fixed 是目前最稳、副作用最少的解法,它不脱离文档流,天然响应视口收缩。
- 父容器(如
#app)必须设min-height: 100vh,不能只写height: 100%,否则键盘弹出时塌陷 - 父容器不能有
transform、overflow: hidden或position: fixed祖先,否则sticky失效且无报错 - iOS 16.4+ 可用
bottom: env(keyboard-inset-bottom, 0px),但必须包裹在@supports (bottom: env(keyboard-inset-bottom))中降级 - 安卓完全不支持
env(keyboard-inset-bottom),fallback 方案是给最外层内容容器加padding-bottom: 60px(按按钮实际高度设),并确保box-sizing: border-box
Flex 布局下内容被压缩、min-height 不生效
用 display: flex; flex-direction: column 做页脚吸底时,键盘弹出会挤压中间区域,导致文字缩成一条线——根本原因是 flex 子项默认 flex-shrink: 1,且未约束高度来源。
- 根容器设
min-height: 100dvh,而不是height: 100vh - 主内容区(如
main)设flex: 1; min-height: 0,显式禁用收缩,防止被压扁 - 底部栏必须和主内容同级、都在 flex 容器内,不能抽成
fixed放在 body 下 - 避免给
body加height: 100vh或min-height: 100vh,这会让scrollIntoView计算失准
真正难的不是写哪条 CSS,而是所有方案都依赖容器结构干净:任意一层 transform、overflow: hidden 或错误的 position 都会让 scrollIntoView 或 sticky 失效,且不报错。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











