// 移动端工具函数模块
// 移动端视口高度处理
export function initMobileViewport() {
function setViewportHeight() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
setViewportHeight();
window.addEventListener('resize', setViewportHeight);
window.addEventListener('orientationchange', () => {
setTimeout(setViewportHeight, 100);
});
// iOS Safari 特殊处理
if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
let lastScrollTop = 0;
window.addEventListener('scroll', () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
if (scrollTop !== lastScrollTop) {
setViewportHeight();
lastScrollTop = scrollTop;
}
}, { passive: true });
}
}
// 移动端输入框焦点处理
export function initMobileInputHandling() {
if (window.innerWidth > 600) return;
document.addEventListener('focusin', (e) => {
if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
setTimeout(() => {
e.target.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 300);
}
});
document.addEventListener('focusout', (e) => {
if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
setTimeout(() => {
const chatArea = document.getElementById('chatArea');
if (chatArea && chatArea.scrollTop === 0) {
return;
}
}, 100);
}
});
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
下面按模块和关键点逐条解释,并给出一些注意事项和改进建议。
总体说明
- 这是一个针对移动端常见问题(视口高度变化、软键盘弹出导致布局/滚动问题、输入框聚焦时滚动)提供的工具函数模块,导出了两个函数:initMobileViewport 和 initMobileInputHandling。调用后会在全局添加一些事件监听以调整样式变量和滚动行为。
- initMobileViewport() 功能概览
- 计算并设置一个 CSS 变量 --vh,其值等于当前 window.innerHeight 的 1%(以 px 为单位)。通过 CSS 使用这个变量可以更可靠地模拟 1vh,避免移动浏览器地址栏/工具栏显示与隐藏导致的布局抖动。
实现细节
- const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh',
${vh}px);- 把 window.innerHeight 的 1% 存为 --vh,比如 innerHeight = 800px 则 --vh = "8px"。在 CSS 中可写 height: calc(var(--vh) * 100) 来替代 100vh。
- 立即调用 setViewportHeight(),确保初次渲染时就设置好变量。
- window.addEventListener('resize', setViewportHeight);
- 当窗口尺寸变化(包括地址栏收起/展开在某些浏览器触发 resize)时重新计算。
- window.addEventListener('orientationchange', () => { setTimeout(setViewportHeight, 100); });
- 旋转设备时延迟 100ms 再计算,给浏览器重绘留时间。
iOS Safari 的特殊处理
- if (/iPhone|iPad|iPod/.test(navigator.userAgent)) { ... }
- 针对 iOS 设备,额外监听 scroll 事件并在滚动发生时重新计算 --vh。原因是 iOS 在地址栏隐藏/显示或滚动时,window.innerHeight 的变化可能不会通过 resize/orientation 及时捕获到。
- 使用 lastScrollTop 来避免在每次 scroll 事件都频繁调用 setViewportHeight。
- scroll 监听使用 { passive: true },以便改善滚动性能(表明回调不会调用 preventDefault)。
为什么要这样做(背景)
- 移动浏览器的 100vh 在地址栏/工具栏显示隐藏时并不稳定,导致元素高度计算不正确。用 --vh 代替可以用 JS 获取真实的 innerHeight,避免布局被“拉伸/压缩”。
- 软键盘弹出、地址栏隐藏/显示、屏幕旋转等都会改变可视高度,用这个变量可以统一处理。
- initMobileInputHandling() 功能概览
- 处理移动端输入框(input/textarea)聚焦时的滚动,避免键盘遮挡输入框。仅在 window.innerWidth <= 600 时才启用(假定为移动设备或窄屏)。
实现细节
- if (window.innerWidth > 600) return; —— 超过 600px 不启用。
- document.addEventListener('focusin', (e) => { ... });
- 当 focusin 事件目标是 textarea 或 input 时,setTimeout 延迟 300ms 后调用 e.target.scrollIntoView({ behavior: 'smooth', block: 'center' }),将焦点元素滚动到可视区域中间位置,避免被键盘遮挡。
- 延迟是为了让软键盘弹出、布局变化先发生再滚动,从而获得正确的滚动位置。
- document.addEventListener('focusout', (e) => { ... });
- 对 focusout 做了一个延迟 100ms 的回调,获取 id 为 'chatArea' 的元素并检查它的 scrollTop === 0 就直接 return。
- 实际上当前代码在 focusout 中没有做其它操作(只有一个条件判断和返回),因此这段 focusout 逻辑并不会对页面状态产生明显的影响——看起来像是未完成或预留的逻辑。
注意、潜在问题与改进建议
- CSS 使用方法:
- 在 CSS 中要用:height: calc(var(--vh, 1vh) * 100); 或 min-height: calc(var(--vh, 1vh) * 100); 这样就能用 JS 计算的 --vh 替代原生 100vh。
- 用户代理判断不可靠:
- /iPhone|iPad|iPod/.test(navigator.userAgent) 的检测有局限(例如 iPadOS 有时呈现为 Mac 的 UA),建议使用更稳健的能力检测或结合 navigator.maxTouchPoints 等。
- visualViewport 更精确:
- 现代浏览器提供 window.visualViewport.height,可直接反映可视区域(包括软键盘弹出后的高度),通常比 window.innerHeight 更可靠。可优先使用 visualViewport 并监听 visualViewport 的 resize/scroll 事件。
- 事件清理(内存泄漏):
- 这两个函数直接在全局注册事件监听,没有返回清理函数。如果组件式使用或多次调用,可能造成重复绑定或内存问题。建议返回一个 teardown 函数用于移除监听。
- focusin/focusout 中的 target 判定:
- 目前直接用 e.target.tagName === 'TEXTAREA' 等判断,tagName 是大写且有可能 target 不是 HTMLElement。建议使用 e.target instanceof HTMLElement && e.target.matches('input, textarea, [contenteditable="true"]')。
- focusin 滚动策略可能导致体验问题:
- scrollIntoView({ behavior: 'smooth' }) 在有些情况下会与软键盘动画冲突,建议根据 visualViewport 或在键盘完全弹出后再滚动,或使用 scrollIntoViewIfNeeded(非标准)或平滑/瞬时滚动选项。
- focusout 逻辑不完整:
- 当前 focusout 只检查 chatArea 并在 scrollTop === 0 时 return,但没有其它处理,可能是未完成。应该明确期望行为(恢复滚动、调整底部间距、隐藏占位符等)。
- mobile breakpoint 固定值:
- 用 window.innerWidth > 600 判断是否为移动端简单但不准确,建议用 CSS 媒体查询或 matchMedia,或更严格的用户场景判断。
总结(一句话)
- initMobileViewport 用于把真实窗口高度以 CSS 变量的形式暴露(解决移动端 100vh 问题),并在 resize/orientation/某些 iOS 滚动时更新;initMobileInputHandling 在窄屏设备上监听输入框聚焦并把输入框滚动到可视中心,避免被软键盘遮挡,但其中 focusout 部分看起来不完整且两函数都可进一步增强(使用 visualViewport、更稳健的设备检测、事件清理等)。
如果需要,我可以:
- 把这两个函数改写成返回 cleanup 函数的版本;
- 改用 visualViewport 并修复 focusout 的逻辑示例;
- 或给出如何在 CSS 中使用 --vh 的具体示例。需要哪种优化?
评论加载中...