聊天气泡每次都被软键盘盖住,你监听 visualViewport.height 算了半天——今天这件事在 viewport meta 里用一行 HTML 配完
你的聊天界面底部有个固定输入框,用户一点,软键盘弹上来,输入框要么被压在键盘底下,要么页面莫名往下滚一段。你查了三天,最后在 JS 里监听 visualViewport 的 resize,用 window.innerHeight 减掉 visualViewport.height 硬算键盘高度,再手动给输入框加 translateY。能跑,但每次键盘动画跟不上,iOS 和安卓表现还不一样。
换个思路:键盘弹起时该动哪个视口,本来就是浏览器自己的决定,而现在的 CSS Viewport 规范给了你一个开关,写在 viewport meta 里,一行 HTML。
结论先说:默认值 resizes-visual 只缩视觉视口、不动布局视口,所以你的 position: fixed 元素不会跟着键盘上移。做全屏应用外壳(底部吸着工具栏/输入框/录音按钮)的话,把它改成 resizes-content,布局视口会一起缩短,100vh 报出变矮后的高度,吸在 bottom: 0 的元素自动坐到键盘上方——不需要一行 JS,也不需要自己量键盘高度。
先分清两个视口。移动端一个页面同时维护两个盒子:布局视口(layout viewport)是 CSS 解析的依据,100vh 和 fixed 元素都钉在它上面;视觉视口(visual viewport)是当前屏幕上真正可见的那一片,用户捏合缩放或浏览器 UI 吃掉空间时会变。键盘历来可以缩其一、缩两者或都不缩,以前选哪个是浏览器说了算,现在写进 meta 让你选。
三个取值,各自的取舍很明确:
- resizes-visual(默认):只缩视觉视口,布局不变,键盘只是把用户能看到的部分切掉。fixed 元素原地不动。手机浏览器就是这样,Chrome 108 在 2022 年从老行为切到它,跟 Safari 对齐,为的是视口相关单位保持稳定——键盘开关不影响 100vh。
- resizes-content:两个视口都缩,键盘在的时候布局视口变矮,100vh 报告变矮后的值,fixed 元素按新高度重排。想要底部工具栏停在键盘上方,选它。
- overlays-content:两个都不缩,键盘画在页面上层,布局完全不动,等价于 Virtual Keyboard API 的 overlaysContent = true。自己画壳、想让键盘像悬浮面板时用它。
怎么选?按机制选,别凭感觉。普通文档、文章页、设置页,保持默认 resizes-visual,让用户自己把输入框滚进视野,CSS 高度不抖;改成 resizes-content 会带来真实重排,反而破坏稳定性。全高应用外壳、底部钉着工具栏的那种,用 resizes-content,代价是键盘开关会触发一次重排——这是你为了想要的布局付的价。自己画 chrome、要做悬浮键盘观感的,才考虑 overlays-content,同时你得自己承担键盘盖住的那块内容。
一个正在发生的变化值得记一下。WebKit 上游已经实现了 interactive-widget 的解析,Bramus 在 9 月 11 日的博文里确认了这一点,并放出了演示页。但要区分”引擎实现”和”浏览器发布”:目前还没确认它会进 Safari,WebKit 是引擎,Safari 是产品,两者常常差好几个月。所以对 Safari,保守做法是不赌它,用 @supports 或运行时特性检测兜底,或者干脆接受老行为。
支持面也顺带对齐一下:interactive-widget 在 Chrome for Android 108+ 全量支持,Firefox for Android 133+,Samsung Internet 21+,WebView Android 108+;桌面端的 Chrome、Edge、Firefox、Safari 一律不支持——这本来就是移动端软键盘的问题。Safari on iOS 不支持,WebView on iOS 也不支持。
今天的下一步很具体:打开你项目的入口 HTML,找到 viewport meta,如果页面是全高应用外壳并且底部吸着工具栏,加上 interactive-widget=resizes-content,先在 Android Chrome 真机上点一下输入框验证工具栏有没有被盖住;如果是普通文档站,什么都别改,保持默认就对了。
评论区
登录后可评论。