IOS 瀏覽器頁面佈局錯位(如:點不到)的分析與解決

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);
};

後續

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章