浮动元素被软键盘顶起是因fixed锚定视觉视口,ios用env(keyboard-inset-bottom)避让(需viewport-fit=cover且@supports包裹),安卓不支持;现代浏览器优先监听focusin+visualviewport.resize动态调整,兼容性方案用absolute模拟fixed并配合padding-bottom兜底。

浮动元素(position: fixed)被软键盘顶起,不是 CSS 写错了,而是浏览器把定位锚点绑在了「视觉视口」上——键盘一弹,视口高度压缩,但 fixed 元素不重算位置,结果就飘在键盘上方。纯 CSS 无法感知键盘状态,必须分层应对。
用 env(keyboard-inset-bottom) 做 iOS 原生避让(16.4+)
这是目前最轻量、无 JS 的方案,但只对 iOS Safari 16.4 及以上有效:
- 必须加 viewport 声明:
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> - 写法是
bottom: env(keyboard-inset-bottom, 0px),不是calc(100vh - env(keyboard-inset-bottom))或混用svh - 必须用
@supports (bottom: env(keyboard-inset-bottom))包裹,否则旧版 Safari 会整条规则失效甚至解析异常 - 安卓完全不支持,不能把它当通用解
监听 focusin + visualViewport 动态调整(现代浏览器首选)
resize 事件在 iOS 上漏触发严重,focusin 才是真正可靠的入口点,配合 visualViewport.height 能拿到实时可视高度:
- 页面加载时记录基准值:
const baseHeight = visualViewport.height - 监听
visualViewport.addEventListener('resize', () => {...}),计算差值作为键盘高度 - 优先用
transform: translateY(${kbHeight}px)移动元素,比改bottom更少触发重排 - 不支持
visualViewport的环境(如 iOS 15.3、部分安卓 WebView)必须降级到focusin后读window.innerHeight
用 position: absolute 模拟 fixed(兼容性最广)
这不是简单换一个 position 值,而是要控制祖先容器的稳定性:
- 根容器(如
#app)设position: relative; min-height: 100vh;,禁用body滚动:body { overflow: hidden; } - 底部区域用
position: absolute; bottom: 0; width: 100%; -
focusin触发后加setTimeout(() => { ... }, 0),确保 DOM 更新完成再读getBoundingClientRect().bottom - 计算
bottom值用window.innerHeight - inputRect.bottom,不是固定像素值 -
blur后务必清除内联样式:el.style.removeProperty('bottom'),再恢复原始定位
兜底:给内容容器加 padding-bottom 预留空间
这是所有方案里兼容性最好、副作用最小的保底手段:
- 在最外层内容容器(如
#app)上设padding-bottom: 60px(按你的底部元素高度来),并确保box-sizing: border-box - 避免直接作用于
body,margin-bottom会引发外边距合并,padding-bottom更可控 - 若页面有 JS 控制键盘状态,记得同步更新该值;否则键盘弹出后内容仍可能被顶穿
- 响应式场景慎用
calc(),部分老安卓 WebView 不认100vh - 100%这类表达式
真正难的不是单个方案,而是组合时机:iOS 键盘收起有延迟,blur 后必须延时 300ms 再恢复;安卓某些 WebView 会把 focusin 和 resize 一起触发,但顺序不可靠;visualViewport 在微信内置浏览器中可能被禁用。别指望一个 CSS 属性或一次监听搞定全部机型。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











