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

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

IOS 瀏覽器軟鍵盤的拉起與收縮、微信 IOS 瀏覽器底部導航條的顯示與隱藏,很容易致使頁面佈局錯位(相對窗體的絕對定位元素):git

  • 明明按鈕在這裏,卻要在上面一點兒點擊屏幕才能點到它
  • 明明彈框是居中顯示的,卻向上偏移了不少,致使下面不少空白
  • 明明是固定浮動在某個位置,卻點不到它

1. Android 與 IOS 的差別

  • 在 Android 中,軟鍵盤的彈起與收縮會觸發 window 對象的 resize 事件,而 IOS 不會
  • 微信 IOS 瀏覽器底部導航條的顯示與隱藏會觸發 window 對象的 resize 事件,而 Android 中沒有底部導航條

2. IOS 裏的一些特性

  • 爲了達到極致的體驗,IOS 瀏覽器不少特性是不遵循 W3C 規範的
  • 軟鍵盤的彈起與收縮不會觸發 window 對象的 resize 事件
  • 軟鍵盤收縮後,固定定位的元素處於錯位狀態,須要滑動頁面後才能刷新頁面恢復到正常狀態

3. 具體狀況分析

無論是 IOS 瀏覽器軟鍵盤的拉起與收縮,仍是微信 IOS 瀏覽器底部導航條的顯示與隱藏,都是改變的 window 窗體的大小。github

微信 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 刷新屏幕以後,再顯示彈框。code

新建 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/blogsblog

做者:深予之 (@senntyou)事件

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

相關文章
相關標籤/搜索