检测Safari浏览器

252

如何使用JavaScript检测Safari浏览器?我尝试了下面的代码,但它不仅检测到了Safari浏览器,还检测到了Chrome浏览器。

function IsSafari() {

  var is_safari = navigator.userAgent.toLowerCase().indexOf('safari/') > -1;
  return is_safari;

}

1
一些与文件提交相关的JS代码在Safari上运行不同,在Chrome上正常运行。 - Tomas
1
你几乎肯定应该测试API之间的任何差异。除了Safari和Chrome之外,还有其他基于WebKit的浏览器。 - Quentin
20
有很多原因可能希望检测浏览器。例如,截至本文撰写时,在Safari中某些SVG引擎的方面(如过滤器)已经损坏,但在Chrome中工作正常。 - Paul Legato
有时候你就是无法修复 bug,因为你无法重现它(我没有 Mac 访问权限)。我已经在 Midori 上解决了问题(一些 BlobBuilder/Blob 的 sendAsBinary shim 问题),但客户说文件上传仍然存在问题,所以我能想到的最好的办法就是删除 Safari 支持并使用 iframes(就像旧版 IE 一样)。 - llamerr
1
可能是重复的问题:如何检测Chrome和Safari浏览器(Webkit) - anik4e
23个回答

7

仅适用于 Safari,不包括 Chrome:

在试用其他代码后,我没有找到任何适用于新旧版本 Safari 的代码。

最终,我编写了此代码,对我非常有效:

var ua = navigator.userAgent.toLowerCase(); 
var isSafari = false;
try {
  isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || safari.pushNotification);
}
catch(err) {}
isSafari = (isSafari || ((ua.indexOf('safari') != -1)&& (!(ua.indexOf('chrome')!= -1) && (ua.indexOf('version/')!= -1))));

//test
if (isSafari)
{
  //Code for Safari Browser (Desktop and Mobile)
  document.getElementById('idbody').innerHTML = "This is Safari!";
}
else
{
  document.getElementById('idbody').innerHTML = "Not is Safari!";
}
<body id="idbody">
</body>


6
我注意到只有一个单词能够区分Safari - "版本"。所以这个正则表达式将会完美地工作:
/.*Version.*Safari.*/.test(navigator.userAgent)

5

我使用这个

function getBrowserName() {
    var name = "Unknown";
    if(navigator.userAgent.indexOf("MSIE")!=-1){
        name = "MSIE";
    }
    else if(navigator.userAgent.indexOf("Firefox")!=-1){
        name = "Firefox";
    }
    else if(navigator.userAgent.indexOf("Opera")!=-1){
        name = "Opera";
    }
    else if(navigator.userAgent.indexOf("Chrome") != -1){
        name = "Chrome";
    }
    else if(navigator.userAgent.indexOf("Safari")!=-1){
        name = "Safari";
    }
    return name;   
}

if( getBrowserName() == "Safari" ){
    alert("You are using Safari");
}else{
    alert("You are surfing on " + getBrowserName(name));
}

5
最简单的答案:
function isSafari() {
 if (navigator.vendor.match(/[Aa]+pple/g).length > 0 ) 
   return true; 
 return false;
}

3
这段代码的意思是:它能够正常运行,但没有使用正则表达式会更简单:navigator.vendor.toLowerCase().indexOf('apple') > -1。其中,navigator.vendor表示浏览器供应商的名称,.toLowerCase()将其转换为小写字母,.indexOf('apple')表示查找字符串中是否包含"apple",如果包含,则返回该子字符串的第一个字符在原始字符串中的索引值。最后,判断索引是否大于-1,若是则表示包含"apple",否则不包含。 - fviktor
6
更简单的写法是:if (navigator.vendor.match(/apple/i)) { ... },意思是如果浏览器的供应商包含"apple"(不区分大小写),则执行括号内的代码。 - Brad
这在iOS 13.3.1上的Firefox中不起作用,因为它显示为Apple Computer,inc。 - chrizonline
3
这可能是你想要的,因为iOS上的每个浏览器都是伪装成Safari的。 - wortwart
如果你愿意的话,甚至可以执行navigator.vendor.indexOf("pple"),但实际上并没有速度差异。 - Infigon

4

上面答案的正则表达式已进行修改(详见此处)

var isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent);
  • crios - 谷歌浏览器
  • fxios - 火狐浏览器

2

我知道这个问题很老了,但我还是想发布答案,因为它可能会对某些人有所帮助。以上解决方案在某些边缘情况下失败了,因此我们不得不以一种分别处理iOS、桌面和其他平台的方式来实现它。

function isSafari() {
    var ua = window.navigator.userAgent;
    var iOS = !!ua.match(/iP(ad|od|hone)/i);
    var hasSafariInUa = !!ua.match(/Safari/i);
    var noOtherBrowsersInUa = !ua.match(/Chrome|CriOS|OPiOS|mercury|FxiOS|Firefox/i)
    var result = false;
    if(iOS) { //detecting Safari in IOS mobile browsers
        var webkit = !!ua.match(/WebKit/i);
        result = webkit && hasSafariInUa && noOtherBrowsersInUa
    } else if(window.safari !== undefined){ //detecting Safari in Desktop Browsers
        result = true;
    } else { // detecting Safari in other platforms
        result = hasSafariInUa && noOtherBrowsersInUa
    }
    return result;
}

2

记录一下,我发现最安全的方法是实现来自这个回答的浏览器检测代码中的Safari部分:

Original Answer翻译成"最初的回答"

const isSafari = window['safari'] && safari.pushNotification &&
    safari.pushNotification.toString() === '[object SafariRemoteNotification]';

当然,处理特定于浏览器的问题的最佳方式始终是进行功能检测(如果可能的话)。虽然使用上述代码片段仍优于代理字符串检测。

原始答案:最初的回答


1
这在 iPhone OS 13_4_1 上的 Safari/604.1 版本/13.1 Mobile/15E148 上无法工作。 - mindo

1
这个独特的“问题”完全表明浏览器是Safari(信不信由你)。
if (Object.getOwnPropertyDescriptor(Document.prototype, 'cookie').descriptor === false) {
   console.log('Hello Safari!');
}

这意味着在Safari中,cookie对象描述符设置为false,而在其他所有浏览器中则为true,这实际上给我在另一个项目中带来了困扰。愉快的编程!

似乎不再正确。同时在Firefox上崩溃,显示“Object.getOwnPropertyDescriptor(...)未定义”。 - Offirmo

1

基于@SudarP的答案。

在2021年第三季度,这个解决方案将在Firefox (Uncaught TypeError: navigator.vendor.match(...) is null)和Chrome (Uncaught TypeError: Cannot read properties of null (reading 'length'))中失败;

因此,这里有一个修复并更短的解决方案:

function isSafari() {
  return (navigator.vendor.match(/apple/i) || "").length > 0
}

1

我测试了#Christopher Martin发布的代码,它报告我的浏览器为Chrome,因为它在测试Edge之前先测试Chrome,否则会对旨在识别Chrome的测试回答true。 我修改了他的答案以纠正这个缺陷和另外两个缺陷,即:

  1. Edge的缩写用户代理子字符串
  2. MSIE的非常老的字符串

将代码转换为函数得到以下函数和测试脚本,通过调试控制台报告。

    <script type="text/javascript">
    function BasicBrowserSniffer ( )
    {
        if ( navigator.userAgent.match ( /edge\//i ) ) {
            return 'edge/edgehtml';
        }
        if ( navigator.userAgent.match ( /edg\//i ) ) {
            return 'edge/edgehtml';
        }
        else if ( navigator.vendor.match ( /google/i ) ) {
            return 'chrome/blink';
        }
        else if ( navigator.vendor.match ( /apple/i ) ) {
            return 'safari/webkit';
        }
        else if ( navigator.userAgent.match ( /firefox\//i ) ) {
            return 'firefox/gecko';
        }
        else if ( navigator.userAgent.match ( /trident\//i ) ) {
            return 'ie/trident';
        }
        else if ( navigator.userAgent.match ( /msie\//i ) ) {
            return 'ie/trident';
        }
        else
        {
            return navigator.userAgent + "\n" + navigator.vendor;
        }
    };  // BasicBrowserSniffer function

    console.info ( 'Entering function anonymous DocumentReady function' );
    console.info ( 'User Agent String   = ' + navigator.userAgent.toLowerCase ( ));
    console.info ( 'User Agent Vendor   = ' + var uav = navigator.vendor.toLowerCase ( );
    console.info ( 'BasicBrowserSniffer = ' + BasicBrowserSniffer ( ) );
    console.info ( 'Leaving function anonymous DocumentReady function' );
</script>

这段代码并没有改进。正如我在帖子中所说的,它不是关于检测浏览器名称,而是浏览器引擎。你的代码检测到了使用blink引擎的新版Edge,将其识别为edgehtml,而这是与旧版Edge完全不同的渲染引擎。Blink版本在UA中使用edg而不是edge来区分。 - Christopher Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接