Telegram React迷你应用虚拟键盘关闭后视口高度无法恢复问题
解决Telegram迷你应用WebView中关闭虚拟键盘后底部出现黑色空白的问题
问题描述
我用React开发Telegram迷你应用,通过WebView在Telegram中展示,遇到以下问题:
- 重现步骤:
- 创建多个输入框
- 点击最后一个输入框,虚拟键盘弹出,页面滚动至聚焦的输入框
- 关闭键盘后,视口无法恢复初始高度,屏幕底部出现黑色空白区域
默认视图:
键盘弹出状态:
键盘关闭状态:
相关代码:
<div className="bg-white h-[100vh] overflow-hidden relative flex flex-col gap-10"> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> </div>
核心原因
问题根源在于使用100vh设置容器高度。Telegram的WebView在虚拟键盘弹出时会调整可见视口高度,但vh单位是基于页面加载时的初始视口高度计算的,键盘关闭后不会自动更新这个值,导致容器高度和实际可见视口不匹配,从而出现底部黑色空白。
解决方法
方法1:用动态视口单位dvh替换vh
现代浏览器支持dvh(动态视口高度)单位,它会实时跟随当前可见视口高度变化,完美适配键盘弹出/关闭的场景,修改代码如下:
<div className="bg-white h-[100dvh] overflow-hidden relative flex flex-col gap-10"> {/* 输入框内容保持不变 */} </div>
方法2:监听窗口resize事件手动调整高度
如果需要兼容不支持dvh的环境,可以通过监听窗口大小变化事件(键盘弹出/关闭会触发resize)来手动更新容器高度:
import { useEffect, useRef } from 'react'; function App() { const containerRef = useRef(null); useEffect(() => { const updateHeight = () => { if (containerRef.current) { containerRef.current.style.height = `${window.innerHeight}px`; } }; window.addEventListener('resize', updateHeight); // 初始化时设置一次高度 updateHeight(); return () => window.removeEventListener('resize', updateHeight); }, []); return ( <div ref={containerRef} className="bg-white overflow-hidden relative flex flex-col gap-10" > <input type="text" placeholder="test" className="bg-red-300" /> <input type="text" placeholder="test" className="bg-red-300" /> {/* 其他输入框 */} </div> ); }
方法3:利用Telegram WebApp API监听键盘状态
Telegram提供了WebApp专属API,可以直接监听键盘的显示/隐藏事件,针对性调整页面:
import { useEffect, useRef } from 'react'; function App() { const containerRef = useRef(null); useEffect(() => { const handleKeyboardToggle = (isVisible) => { if (containerRef.current) { containerRef.current.style.height = isVisible ? `${window.innerHeight}px` : `${window.screen.height}px`; // 关闭键盘后滚动到顶部,确保视口完全恢复 window.scrollTo(0, 0); } }; // 绑定键盘显示/隐藏事件 Telegram.WebApp.onEvent('keyboardShow', () => handleKeyboardToggle(true)); Telegram.WebApp.onEvent('keyboardHide', () => handleKeyboardToggle(false)); // 组件卸载时移除事件监听 return () => { Telegram.WebApp.offEvent('keyboardShow'); Telegram.WebApp.offEvent('keyboardHide'); }; }, []); return ( <div ref={containerRef} className="bg-white h-[100vh] overflow-hidden relative flex flex-col gap-10" > {/* 输入框内容 */} </div> ); }
额外提示
- 如果必须给容器设置
overflow-hidden,可以在键盘关闭后触发一次页面重绘(比如临时添加再移除一个无用的CSS类),强制浏览器更新视口计算 - 测试时尽量用真实的Telegram环境,模拟器可能存在视口计算偏差
内容的提问来源于stack exchange,提问作者Rostyk
相关产品推荐
相关产品推荐

