最近写h5页面,在浏览器顶部和底部的功能栏,会动态隐藏,导致页面高度和可视高度不匹配,导致部分元素被遮挡。
原因
浏览器顶部和底部的功能栏,是占用100vh的高度的。导致页面部分底部被遮挡。
所以常规页面设置vh方式就有问题。


解决方案
我的一个方案就是,通过css的var变量,配合js,动态修改变量的值,以达到页面高度变化时,自动修改css变量。
页面使用的时候,同时使用css变量。
定义了2个变量,--vh和--svh。
--vh是当前页面可视窗口的高度。
--svh是可视窗口的高度 - 安全区域底部高度。也就是手机的底部bar。
使用方式:
- 页面全屏展示,不需要考虑安全区域底部高度
height: calc(var(--vh, 1vh) * 100);
- 页面考虑安全区域底部高度时
height: calc(var(--svh, 1vh) * 100);
完整的js文件如下:
javascript
//使用场景:页面全屏展示,不需要考虑安全区域底部高度
// height: calc(var(--vh, 1vh) * 100);
//使用场景:页面有安全区域底部高度,需要考虑安全区域底部高度
// height: calc(var(--svh, 1vh) * 100);
export function updateVh(height: number) {
// 容器高度
const vh = window.innerHeight * 0.01;
// 去除ios安全区域底部高度
const svh = (window.innerHeight - getSafeAreaInsetBottom()) * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
document.documentElement.style.setProperty('--svh', `${svh}px`);
}
/**
* 获取安全区域底部高度(safe-area-inset-bottom)
* @returns {number} 安全区域底部高度(单位:px,不支持时返回 0)
*/
export function getSafeAreaInsetBottom() {
// 创建临时元素用于计算
const tempElement = document.createElement('div');
// 设置样式:利用 env() 函数获取安全区域值,默认值为 0px(兼容不支持的浏览器)
Object.assign(tempElement.style, {
position: 'fixed', // 脱离文档流,不影响布局
visibility: 'hidden', // 隐藏元素,仅用于计算
paddingBottom: 'env(safe-area-inset-bottom, 0px)', // 核心:应用安全区域值
left: '0',
bottom: '0'
});
// 将临时元素添加到页面
document.body.appendChild(tempElement);
// 获取计算后的样式值(如 "34px")
const computedStyle = window.getComputedStyle(tempElement);
const paddingBottomValue = computedStyle.paddingBottom;
// 移除临时元素(清理痕迹)
document.body.removeChild(tempElement);
// 转换为数字(去除单位),确保返回数值类型
return parseFloat(paddingBottomValue) || 0;
}
function initAndWatchVh() {
// 初始化
let availableHeight = window.innerHeight;
console.log('load', availableHeight);
updateVh(availableHeight);
// IOS需要load以后再触发,不然获取不到安全区域底部高度
window.addEventListener('load', () => {
let availableHeight = window.innerHeight;
updateVh(availableHeight);
});
// 监听窗口大小变化(工具栏隐藏/显示)
window.addEventListener('resize', () => {
availableHeight = window.innerHeight;
console.log('resize', availableHeight);
updateVh(availableHeight);
});
}
initAndWatchVh();