大家好,我是 Immerse,一名獨立開發者、內容創作者、AGI 實踐者。

關注公眾號:沉浸式趣談,獲取最新文章(更多內容只在公眾號更新)

個人網站:https://yaolifeng.com 也同步更新。

轉載請在文章開頭註明出處和版權信息。

我會在這裏分享關於編程獨立開發AI乾貨開源個人思考等內容。

如果本文對您有所幫助,歡迎動動小手指一鍵三連(點贊評論轉發),給我一些支持和鼓勵,謝謝!


最近看到一篇文章,針對於 Safari 和 iOS 版本檢測很不錯,分享出來給大家。

之前都用 User-Agent 一把嗦,但文章提到檢測結果不準確。

兩個方式

  1. User-Agent
  2. 特性檢測

User-Agent 檢測

這個方法就是獲取瀏覽器的 User-Agent,從裏面提取版本信息。

但是有問題,這個結果不準確。

Safari 的 UA 字符串裏有兩個版本號,一個是技術版本,一個是市場版本。很多腳本會把這倆搞混。

還有一點,從 macOS 11 開始,Safari 的 UA 裏系統版本就不更新了,永遠顯示 10.15.7。

所以想從 UA 裏準確獲取版本?基本不可能。

MDN 官方都説了,別依賴 UA 字符串做瀏覽器檢測邏輯,這是個常見 bug 源頭。

特性檢測

蘋果官方推薦: 特性檢測。

就是直接檢查瀏覽器支不支持某個 API 或 CSS 特性。

但它沒法區分所有版本,因為很多特性在好幾個版本里都有。

解決思路

把兩種方法結合起來用, 主要靠特性檢測,UA 檢測作為補充。

第一步:檢測 WebKit 引擎

在 iOS 上,所有瀏覽器都必須用 WebKit,包括 Chrome、Firefox 這些。

所以檢測 WebKit 能幫我們縮小範圍:

// 桌面 Safari 和所有 iOS 瀏覽器
function isWebkit() {
    return 'GestureEvent' in window;
}

// 所有移動端 WebKit 瀏覽器
function isMobileWebKit() {
    return 'ongesturechange' in window;
}

// 只檢測桌面 Safari
function isDesktopWebKit() {
    return typeof window !== 'undefined' && 'safari' in window && 'pushNotification' in window.safari;
}

第二步:檢測特定 iOS 版本

去查 Safari 發佈説明或 WebKit 的更新日誌,找到某個版本新增的特性。

比如我想檢測 iOS 17.0,發現這個版本加入了 contain-intrinsic-size 支持。

那就檢測這個特性:

// iOS 17.0+ 返回 true
const isAtLeastiOS17 = CSS.supports('contain-intrinsic-size', '100px');

如果要檢測具體的小版本,可以配合下一個版本的特性來排除。

比如 ManagedMediaSource 是在 iOS 17.1 才有的:

const supportsManagedMediaSource = 'ManagedMediaSource' in window;

// 只匹配 iOS 17.0
function isOnlyiOS170() {
    return isAtLeastiOS17 && !supportsManagedMediaSource;
}

if (isMobileWebKit()) {
    if (isOnlyiOS170()) {
        // 這是 iOS 17.0
    }
}

第三步:真機測試

理論歸理論,實際測試才是王道。

踩坑:

iOS 17.6 的發佈説明裏説支持 CSS 的 safe 關鍵字,用 @supports 檢測也返回 true。

結果真機上一跑,根本不生效。

這種情況下,只能換個思路,檢測實際的渲染效果:

<video src="https://qncdn.mopic.mozigu.net/work/143/25/8fe6997ae26b491d/safecenter.mp4" controls></video>

const isSafeKeywordSupported = () => {
    const container = document.createElement('div');
    const child = document.createElement('span');

    child.textContent = 'Evil Martians';

    container.style.display = 'flex';
    container.style.justifyContent = 'safe center';
    container.style.width = '5%';
    container.style.position = 'absolute';
    container.style.top = '-9999px';
    container.style.left = '-9999px';

    container.appendChild(child);
    document.body.appendChild(container);

    const containerRect = container.getBoundingClientRect();
    const childRect = child.getBoundingClientRect();
    const isCroppedOnLeft = childRect.left < containerRect.left;

    document.body.removeChild(container);
    return !isCroppedOnLeft;
};

通過檢查元素的實際渲染位置,判斷特性是不是真的生效了。

第四步:配合 UA 檢測

有時候特性檢測也不夠用。

比如要區分 iPad 和其他設備。

iPad 的 UA 字符串跟 macOS 上的 Safari 一模一樣。

但如果 UA 顯示是 macOS,特性檢測又顯示是移動端 WebKit,那就能判斷出這是 iPad:

// 檢測 iPadOS
function isiPad() {
    return isDesktopWebKit() && isMobileWebKit();
}

幾個關鍵點

WebKit 不等於 Safari,iOS 上所有瀏覽器都用 WebKit。

主要用特性檢測,UA 檢測只是補充。

多看 Safari 和 WebKit 的發佈説明,但也別全信,因為有些變更根本沒寫進去。

真機測試不能省,有些 bug 只有在實際設備上才能發現。

有時候 @supports 會撒謊,瀏覽器説支持但實際不行,這時候得檢查實際渲染效果。

寫在最後

核心思路就是:特性檢測為主,UA 檢測為輔,真機測試驗證。

參考資料

  • https://developer.apple.com/documentation/safari-release-notes
  • https://webkit.org/
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Browser_detection_using_the_user_agent