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 許可證)