IOS 浏览器页面布局错位(如:点不到)的分析与解决
IOS 浏览器软键盘的拉起与收缩、微信 IOS 浏览器底部导航条的显示与隐藏,很容易导致页面布局错位(相对窗体的绝对定位元素):
明明按钮在这里,却要在上面一点儿点击屏幕才能点到它
明明弹框是居中显示的,却向上偏移了很多,导致下面很多空白
明明是固定浮动在某个位置,却点不到它
1. Android 与 IOS 的差异
在 Android 中,软键盘的弹起与收缩会触发 window 对象的 resize 事件,而 IOS 不会
微信 IOS 浏览器底部导航条的显示与隐藏会触发 window 对象的 resize 事件,而 Android 中没有底部导航条
2. IOS 里的一些特性
为了达到极致的体验,IOS 浏览器很多特性是不遵循 W3C 规范的
软键盘的弹起与收缩不会触发 window 对象的 resize 事件
软键盘收缩后,固定定位的元素处于错位状态,需要滑动页面后才能刷新页面恢复到正常状态
3. 具体情况分析
不管是 IOS 浏览器软键盘的拉起与收缩,还是微信 IOS 浏览器底部导航条的显示与隐藏,都是改变的 window 窗体的大小。
微信 IOS 浏览器底部导航条的显示与隐藏跟软键盘的拉起与收缩是差不多的,但微信 IOS 浏览器底部导航条还有一个很大的特点:
在单页面应用(SPA)中,当路由发生变化时,底部导航条会一下子就显示,而这很难确定是先渲染了页面还是先显示了底部导航条,
这也很容易导致元素布局错位。
4. 怎么解决
4.1 监听键盘弹起与收缩,自动做一些操作
新建 watch-keyboard.js 脚本,引入到页面中。
当页面中键盘弹起时,body 会有 keyboard-active class,可以根据这个隐藏一些元素。
import {isIos} from '../utils';
import debounce from 'lodash/debounce';
// 初始高度
const winHeight = window.innerHeight;
// 判断是不是弹起了软键盘
const judgeDistance = 200;
if (!isIos) {
window.addEventListener(
'resize',
debounce(() => {
if (window.innerHeight < winHeight - judgeDistance) {
// 键盘弹起
document.body.classList.add('keyboard-active');
} else {
document.body.classList.remove('keyboard-active');
}
}, 300),
!1
);
}
else {
// IOS 软键盘的弹起与收缩不会触发 `window` 对象的 `resize` 事件,用定时器实现
// 保证能够滚动
document.body.style.minHeight = (winHeight + 2) + 'px';
// 上两次高度记录
let secondLastWinHeight = winHeight;
// 上一次高度记录
let lastWinHeight = winHeight;
setInterval(() => {
const newWinHeight = window.innerHeight;
// 变化结束
if (secondLastWinHeight !== lastWinHeight && lastWinHeight === newWinHeight) {
if (newWinHeight < winHeight - judgeDistance) {
// 键盘弹起
document.body.classList.add('keyboard-active');
} else {
document.body.classList.remove('keyboard-active');
// window 需要滚动一下,让页面刷新一下,否则弹框会出现错位的问题
window.scrollTo(0, window.scrollY ? window.scrollY - 1 : 1);
}
}
secondLastWinHeight = lastWinHeight;
lastWinHeight = newWinHeight;
}, 300); // 可以根据需要调整间隔时间(越小越精确)
}
4.2 监听窗体大小变化,执行一个回调,做更多操作
当软键盘弹起时,又点击了一个按钮,然后显示弹框(如:从底部向上弹出)的时候,这个时候就需要等待软键盘收起之后,IOS 刷新屏幕之后,再显示弹框。
新建 wait-for-stable-win-height.js 脚本,引入到页面中。
import { isIos } from '../utils';
/**
* 等待 window 高度不变了之后执行一个回调函数
*
* @param onComplete 完成的回调
* @param delay 延迟多少时间再判断
* @param interval 定时器间隔时间
*/
export default ({ onComplete, delay = 200, interval = 50 }) => {
setTimeout(() => {
let winHeight = window.innerHeight;
const timer = setInterval(() => {
const newWinHeight = window.innerHeight;
if (winHeight === newWinHeight) {
clearInterval(timer);
if (onComplete) {
if (!isIos) {
setTimeout(() => {
onComplete();
}, 100);
return;
}
// window 需要滚动一下,让页面刷新一下,否则弹框会出现错位的问题
window.scrollTo(0, window.scrollY ? window.scrollY - 1 : 1);
setTimeout(() => {
onComplete();
}, 200);
}
} else {
winHeight = newWinHeight;
}
}, interval);
}, delay);
};
后续
版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)