【问题标题】:Detect if device is iOS检测设备是否为 iOS
【发布时间】:2022-01-15 15:52:42
【问题描述】:

我想知道是否可以检测浏览器是否在 iOS 上运行,类似于使用 Modernizr 进行功能检测(尽管这显然是设备检测而不是功能检测)。

通常我更喜欢功能检测,但我需要确定设备是否是 iOS,因为他们根据这个问题处理视频的方式YouTube API not working with iPad / iPhone / non-Flash device

【问题讨论】:

标签: javascript ios browser browser-feature-detection


【解决方案1】:

检测 iOS

使用iOS 13 iPad both User agent and platform strings are changeddifferentiating between iPad and MacOS seems possible,所以下面的所有答案现在都需要考虑到这一点。

这可能是涵盖 iOS 13 的最短替代方案:

function iOS() {
  return [
    'iPad Simulator',
    'iPhone Simulator',
    'iPod Simulator',
    'iPad',
    'iPhone',
    'iPod'
  ].includes(navigator.platform)
  // iPad on iOS 13 detection
  || (navigator.userAgent.includes("Mac") && "ontouchend" in document)
}

iOS 将是 truefalse

更糟糕的选择:用户代理嗅探

用户代理嗅探更危险,问题经常出现。

在 iPad iOS 13 上,用户代理与 MacOS 13 计算机上的用户代理相同,但如果您忽略 iPad,这可能仍会工作一段时间:

var iOS = !window.MSStream && /iPad|iPhone|iPod/.test(navigator.userAgent); // fails on iPad iOS 13

!window.MSStream 是为了不错误地检测 IE11,请参阅 herehere

注意:navigator.userAgentnavigator.platform 都可以被用户或浏览器扩展伪造。

存在用于更改 userAgent 或平台的浏览器扩展,因为网站使用过于严厉的检测并且经常禁用某些功能,即使用户的浏览器本来可以使用该功能。

为了缓和与用户之间的冲突,建议针对每种情况专门检测您网站所需的确切功能。然后,当用户获得具有所需功能的浏览器时,它已经可以工作而无需额外的代码更改。

检测 iOS 版本

检测iOS版本最常用的方法是parsing it from the User Agent string。但也有特征检测推断*;

我们知道 history API 是在 iOS4 中引入的 - iOS5 中的 matchMedia API - iOS6 中的 webAudio API - WebSpeech APIiOS7 等等。

注意:以下代码不可靠,如果这些 HTML5 功能中的任何一个在较新的 iOS 版本中被弃用,就会中断。您已被警告!

function iOSversion() {

  if (iOS) { // <-- Use the one here above
    if (window.indexedDB) { return 'iOS 8 and up'; }
    if (window.SpeechSynthesisUtterance) { return 'iOS 7'; }
    if (window.webkitAudioContext) { return 'iOS 6'; }
    if (window.matchMedia) { return 'iOS 5'; }
    if (window.history && 'pushState' in window.history) { return 'iOS 4'; }
    return 'iOS 3 or earlier';
  }

  return 'Not an iOS device';
}

【讨论】:

  • 你在第二个 sn-p 中所做的是特征推断,而不是特征检测。功能检测是测试您实际要使用的功能,而您所做的是测试您碰巧知道在特定操作系统版本中引入的功能,并从中推断出操作系统版本。这很脆弱,因为未来版本的 iOS 可能会删除这些功能。
  • 这是写支票的更好方法:var iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
  • 请注意 - navigator.platform 数组在 iPad 模拟器上不起作用,因为它在平台字符串中包含整个短语“iPad Simulator”。
  • 在用户代理字符串中测试 iPad、iPhone 或 iPod 将给出误报,以防用户拥有 Window Phone。此设备上的 Internet Explorer 在用户代理字符串中包含消息“...like iPhone OS...”,因此在此测试中将返回 true。
  • 从 iOS 13 开始,iPad 的用户代理已更改为“Mac OS”,例如:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15 所以这个答案需要更新
【解决方案2】:

在 iOS 13 之后,您应该像这样检测 iOS 设备,因为 iPad 不会被旧方式检测为 iOS 设备(由于新的“桌面”选项,默认启用):

let isIOS = /iPad|iPhone|iPod/.test(navigator.platform)
|| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)

iOS

与其说是真正的解决方案,不如说是一种 hack,但对我来说工作可靠

P.S.如前所述,您可能应该添加 IE 检查

let isIOS = (/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
!window.MSStream

【讨论】:

  • 为什么不使用navigator.userAgent 来检查/iPad|iPhone|iPod/.test(navigator.platform)?似乎navigator.platform 总是为 iPhone iOS 返回“MacIntel”
  • @CharisTheo 因为 iPad 不在 iOS 中的 userAgent >= 13
  • 但您已经在第二次检查中检查 iPad iOS >= 13 还是我遗漏了什么?
  • navigator.maxTouchPoints 在 iOS 中不受支持,因此检查不会为您做任何事情。
  • @PaulC,您是正确的,因为 iOS 12 及更低版本未定义 maxTouchPoints,但由于 iOS 13 支持 maxTouchPoints,因此 kikiwora 走在正确的轨道上。请参阅我的答案。
【解决方案3】:

这会将变量 _iOSDevice 设置为 truefalse

_iOSDevice = !!navigator.platform.match(/iPhone|iPod|iPad/);

【讨论】:

  • 做什么!!做什么?
  • @astronought 双重否定用于转换为布尔值
  • @astronought 砰砰,你是布尔值:D
  • 使用/iPhone|iPod|iPad/.test(navigator.platform)可以避免!!
【解决方案4】:

此处之前的答案均不适用于所有 iOS 版本(包括 iOS 13)上的所有主要浏览器。以下是适用于所有 iOS 版本的 Safari、Chrome 和 Firefox 的解决方案:

var isIOS = (function () {
    var iosQuirkPresent = function () {
        var audio = new Audio();

        audio.volume = 0.5;
        return audio.volume === 1;   // volume cannot be changed from "1" on iOS 12 and below
    };

    var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
    var isAppleDevice = navigator.userAgent.includes('Macintosh');
    var isTouchScreen = navigator.maxTouchPoints >= 1;   // true for iOS 13 (and hopefully beyond)

    return isIOS || (isAppleDevice && (isTouchScreen || iosQuirkPresent()));
})();

请注意,这段代码 sn-p 的编写优先考虑可读性,而不是简洁性或性能。

解释:

  • 如果用户代理包含任何“iPod|iPhone|iPad”,那么显然该设备是 iOS。否则,继续...

  • 任何其他不包含“Macintosh”的用户代理都不是 Apple 设备,因此不能是 iOS。否则,它是 Apple 设备,所以继续...

  • 如果maxTouchPoints 的值为1 或更大,则Apple 设备有触摸屏,因此必须是iOS,因为没有带触摸屏的Mac(感谢kikiwora 提到maxTouchPoints) .请注意,对于 iOS 12 及更低版本,maxTouchPointsundefined,因此我们需要针对该场景使用不同的解决方案...

  • iOS 12 及更低版本有一个 Mac OS 中不存在的怪癖。怪癖是Audio 元素的volume 属性不能成功设置为1 以外的任何值。这是因为 Apple 不允许在 iOS 设备的 Audio 元素上更改音量,但对于 Mac OS 则允许。这个怪癖可以用作区分 iOS 设备和 Mac OS 设备的最终后备方法。

【讨论】:

  • 这似乎会产生实际改变非 iOS 设备上的音量的副作用(以防这对任何人都很重要)
  • @Tspoon,提供的代码 sn-p 创建一个一次性的 Audio 元素(如有必要)。在这种情况下,该元素实际上并不用于播放声音,并且它不会影响您可能在系统中使用的其他 Audio 元素的音量。
【解决方案5】:

如果您使用的是Modernizr,您可以为其添加自定义测试。

无论您决定使用哪种检测模式(userAgent、navigator.vendor 或 navigator.platform),您都可以随时将其打包以便以后使用。

//Add Modernizr test
Modernizr.addTest('isios', function() {
    return navigator.userAgent.match(/(iPad|iPhone|iPod)/g);
});

//usage
if (Modernizr.isios) {
    //this adds ios class to body
    Modernizr.prefixed('ios');
} else {
    //this adds notios class to body
    Modernizr.prefixed('notios');
}

【讨论】:

  • 小心,Modernizr 会自动将添加的测试名称小写。 (在您的示例中, Modernizr.isiOS 永远不会返回 true)。在我看来,lib 的不良行为...
  • 请注意:您可以将return x ? true : false 简化为return Boolean(x) 或只是return !!x
【解决方案6】:

一个简化的、易于扩展的版本。

var iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0;

【讨论】:

  • 如果您还希望它在 iOS 模拟器上运行,您可以使用:navigator.platform.replace(' Simulator', '')
  • 但是不行,原因['str'].indexOf('string') == -1
  • navigator.platform 将是完全“iPad”、“iPhone”或“iPod”,除非模拟器正在运行。
【解决方案7】:

更新:我最初的答案不包括处于 桌面模式 的 iPad(即将推出 iPadOS 13 及更高版本中桌面模式的默认更改)。
这对我的用例来说很好,如果它不适合你,请使用此更新:

// iPhone and iPad including iPadOS 13+ regardless of desktop mode settings

iOSiPadOS = /^iP/.test(navigator.platform) ||
           /^Mac/.test(navigator.platform) && navigator.maxTouchPoints > 4;
  • 这应该是安全的
    • 桌面 Mac 根本不支持触摸事件
    • 或不超过 4 个触摸点(当前 iOS 设备支持 5 个触摸点)
  • 很快因为正则表达式^首先检查平台字符串的起始位置,如果没有“iP”则停止(无论如何都比搜索长UA字符串要快)
  • 它比navigator.userAgent 更安全 检查因为navigator.platform 被伪造的可能性要小得多
  • 检测 iPhone / iPad 模拟器

原始答案:
哇,这里有很多冗长而棘手的代码。请保持简单!

恕我直言,这个速度很快,保存良好,运行良好:

 iOS = /^iP/.test(navigator.platform);

  // or, if you prefer it verbose:
 iOS = /^(iPhone|iPad|iPod)/.test(navigator.platform);

【讨论】:

  • iOS = /^(iPhone|iPad|iPod)/.test(navigator.platform); 而不是这个我会做 iOS = /^(iPhone|iPad|iPod)/.test(navigator.userAgent || navigator.vendor || navigator.platform); 作为后备措施因为在我的情况下 navigator.platform 没有工作,但像后来一样工作正常
  • navigator.platform 没用?那你真的在iOS上吗?检查jeka.info/test/navigator.htmluserAgent 给出误报是因为一些供应商出于任何原因伪造它以模仿 Apple 设备。 vendor 只返回 Google Inc.Apple Computer, Inc. 或什么都不返回(在 Firefox 中)。
【解决方案8】:

运行 iOS 13 的 iPad 将 navigator.platform 设置为 MacIntel 可能值得回答,这意味着您需要找到另一种方法来检测 iPadOS 设备。

【讨论】:

    【解决方案9】:

    检测 iOS(

    社区 wiki,因为编辑队列说它已满,所有其他答案目前都已过时或不完整。

    const iOS_1to12 = /iPad|iPhone|iPod/.test(navigator.platform);
    
    const iOS13_iPad = (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1));
    
    const iOS1to12quirk = function() {
      var audio = new Audio(); // temporary Audio object
      audio.volume = 0.5; // has no effect on iOS <= 12
      return audio.volume === 1;
    };
    
    const isIOS = !window.MSStream && (iOS_1to12 || iOS13_iPad || iOS1to12quirk());
    

    【讨论】:

      【解决方案10】:

      这是我几年前写的,但我相信它仍然有效:

      if(navigator.vendor != null && navigator.vendor.match(/Apple Computer, Inc./) && navigator.userAgent.match(/iPhone/i) || (navigator.userAgent.match(/iPod/i))) 
      
          {
      
              alert("Ipod or Iphone");
      
          }
      
      else if (navigator.vendor != null && navigator.vendor.match(/Apple Computer, Inc./) && navigator.userAgent.match(/iPad/i))  
      
          {
      
              alert("Ipad");
      
          }
      
      else if (navigator.vendor != null && navigator.vendor.match(/Apple Computer, Inc./) && navigator.userAgent.indexOf('Safari') != -1)
      
          {
      
              alert("Safari");
      
          }
      
      else if (navigator.vendor == null || navigator.vendor != null)
      
          {
      
              alert("Not Apple Based Browser");
      
          }
      

      【讨论】:

        【解决方案11】:

        在添加 Modernizr 测试时,您应该尽可能添加功能测试,而不是设备或操作系统。如果需要的话,为 iPhone 添加十个测试并没有错。有些东西就是无法检测到特征。

            Modernizr.addTest('inpagevideo', function ()
            {
                return navigator.userAgent.match(/(iPhone|iPod)/g) ? false : true;
            });
        

        例如,在 iPhone(不是 iPad)上,视频不能在网页上内联播放,它会打开全屏。所以我创建了一个测试'no-inpage-video'

        然后你可以在 css 中使用它(如果测试失败,Modernizr 会在 &lt;html&gt; 标签中添加一个类 .no-inpagevideo

        .no-inpagevideo video.product-video 
        {
             display: none;
        }
        

        这将隐藏 iPhone 上的视频(在这种情况下,我实际上正在做的是显示一个带有 onclick 播放视频的替代图像 - 我只是不希望显示默认的视频播放器和播放按钮)。

        【讨论】:

        【解决方案12】:

        iOS 设备上的用户代理在其中说 iPhone 或 iPad。我只是根据这些关键字进行过滤。

        【讨论】:

        • 还有 iPod Touch 需要考虑。
        • @DouglasGreenshields 正确。忘了那个,但我相信它也会在用户代理中传输其身份。
        • iPad safari 的用户代理将不再包含 iPadOS 13 中的“iPad”。
        【解决方案13】:

        如果你使用 React,有一个很好的库可以解决这类问题:REACT-UGENT。 (使用 ua-parser-js 构建。)

        https://github.com/medipass/react-ugent

        可用的浏览器有:

        chrome、chromium、edge、firefox,即 lynx、safari、opera

        可用的操作系统有:

        android、黑莓、铬操作系统、debian、ios、linux、mac os、ubuntu、unix、windows

        可用设备有:

        控制台、计算机、移动设备、平板电脑、智能电视、可穿戴设备、嵌入式

        易于使用:

        <Ugent browser="safari" os="ios">
          <div>
            This text only shows on Safari on iOS.
          </div>
        </Ugent>
        

        如果你不使用 React,基本上你可以使用 - ua-parser-js

        https://github.com/faisalman/ua-parser-js

        【讨论】:

          【解决方案14】:

          使用更实用的方法稍微更新第一个答案。

              const isIOS = [
                'iPad Simulator',
                'iPhone Simulator',
                'iPod Simulator',
                'iPad',
                'iPhone',
                'iPod',
              ].indexOf(navigator.platform) !== -1;
          

          【讨论】:

          • 在 Brave/Chrome 开发工具移动模拟器中不起作用。我得到MacIntel
          【解决方案15】:

          你也可以使用includes

            const isApple = ['iPhone', 'iPad', 'iPod', 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator',].includes(navigator.platform)
          

          【讨论】:

          • 这错过了“...模拟器”品种。这么多的答案,这么少的充分研究:(
          【解决方案16】:

          无需测试navigator.userAgentnavigator.platform

          const isIOS = typeof navigator.standalone === 'boolean';
          

          navigator.standalone 仅在 iOS Safari 上设置。见MDNSafari HTML Reference

          【讨论】:

          • 如果浏览器是chrome怎么办?
          • @IBG 刚刚尝试过,它在 iOs chrome 上返回“未定义”。所以这个答案是不正确的
          • @IBG 我刚刚在 iPhone (iOS 14.3) 上的 Firefox 38.1 和 iPad (iPadOS 15.0.2) 上的 Chrome 95.0.4638.50 上试过这个,它在两者上都有效
          • @strix25 你确定你输入的代码正确吗?
          【解决方案17】:

          因为navigator.platform已被弃用,最好不要再使用它,我想添加一个其他解决方案。

          您可以通过检查navigator.vendor 在 MacOS 系统上进行过滤。 当结果是Apple Computer, Inc.,你就知道是MacOS了。

          【讨论】:

            【解决方案18】:

            在我的情况下,用户代理不够好,因为在 Ipad 中,用户代理与 Mac OS 中的相同,因此我不得不做一个讨厌的把戏:

            var mql = window.matchMedia("(orientation: landscape)");
            
            /**
             * If we are in landscape but the height is bigger than width
             */
            if(mql.matches && window.screen.height > window.screen.width) {
                // IOS
            } else {
                // Mac OS
            }
            

            【讨论】:

              【解决方案19】:

              var isiOSSafari = (navigator.userAgent.match(/like Mac OS X/i)) ? true: false;

              【讨论】:

              • 这个三元运算符没用; test 返回一个布尔值,可以替换 match
              【解决方案20】:

              为了检测 iOS 版本,必须使用这样的 Javascript 代码解构用户代理:

               var res = navigator.userAgent.match(/; CPU.*OS (\d_\d)/);
                  if(res) {
                      var strVer = res[res.length-1];
                      strVer = strVer.replace("_", ".");
                      version = strVer * 1;
                  }
              

              【讨论】:

                猜你喜欢
                • 2019-02-23
                • 1970-01-01
                • 2018-03-08
                • 2012-05-27
                • 2015-07-19
                • 2015-11-11
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多