目前浏览器检测 (Browser detect) 一般有两种方法:

  • 基于userAgent的值的检测;
  • 基于对象的检测,即object detection;

而第一种方法,一般来说效果并不是非常理想,因为许多浏览器都允许用户更改userAgent的值,起因要追究于第一次浏览器大战(PPK on Javascript p57)。因此,这一类的检测都不是万能的,无论是基于navigator.userAgent还是navigator.vendor等,浏览器总有办法绕过这一层检测。

PPK 在他的一篇文章中提供的检测脚本其实也是基于navigator的,但是他在整个脚本的设计上非常细致,覆盖了许多细节,看了之后收获很大。首先使用了下ppk的检测脚本,博客首页右方检测浏览器的功能正是使用ppk的脚本。

例子

当时看脚本的时候还是有点迷糊,因为脑子里面对各个浏览器的userAgent内容非常模糊,其实自己就用过Firefox、Chrome、IE、Opera等几个浏览器,没试过Safari、Konqueror、iCab等等。回头看完了脚本,我就把自己电脑上有的浏览器的userAgent都看了一遍并列了出来:

Opera/9.80 (Windows NT 6.1; U; Edition IBIS; en) Presto/2.6.30 Version/10.60
Mozilla/5.0 (Windows NT 6.1; rv:2.0b8pre) Gecko/20101029 Firefox/4.0b8pre
Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.41 Safari/534.7
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0; AskTB5.6)

然后是脚本检测的结果分别是:

You're using Opera 9.8 on Windows !
You're using Firefox 4 on Windows !
You're using Chrome 7 on Windows !
You're using Explorer 9 on Windows !

对比一下,Firefox的版本是4.0b8pre, 显示的是Firefox 4,因为脚本里面使用的方法是parseFloat,去掉了非数字的字符, 后面的Chrome以及IE也一样。而对于Opera,因为脚本里面是根据浏览器的identity来搜索版本号的,而Opera的identity正是Opera;因此结果成了parseFloat(9.80) => 9.8。不知道这里的9.80是代表什么信息。

脚本分析

BrowserDetect

下面回到脚本的代码上,browserDetect.js定义了一个BrowserDetect对象,如下所示:

var BrowserDetect = {
    init: function () {
        //...
    },
    
    searchString: function(data) {
        //...
    },
    
    searchVersion:function(dataString) {
        //...
    },
    
    dataBrowser: [
        //...
    ],
        
    dataOS: [
        //...
    ]
}

BrowserDetect对象一共提供了三个方法以及两个数组。其中,init方法初始化BrowserDetect对象属性,通过调用searchString和searchVersion得到以下属性:

  • 浏览器名字:BrowserDetect.browser
  • 浏览器版本:BrowserDetect.version
  • 操作系统名字:BrowserDetect.OS

dataBrowser

而dataBrowser数组中包含一些浏览器对象信息,可以方便脚本检测用户所使用的浏览器,它的基本语法结构是:

dataBrowser: [
    {
        prop: window.opera,
        identity: "Opera" // note: no comma
    },
    {
        string: navigator.userAgent,
        subString: "MSIE",
        identity: "Explorer",
        versionSearch: "MSIE" // note: no comma
    } // note: no comma
];

dataBrowser允许以下几种属性:

  1. string和subString:指在string中搜索subString,如果找到,意味着浏览器被识别。
  2. prop:指看看prop属性是否支持,如果支持,意味着浏览器被识别。
  3. identity:最终将这个属性的值赋值给BrowserDetect.browser
  4. versionSearch:用来查找版本号,如果没有定义,则用identity代替。

注意,每个浏览器对象必须包含1或者2(不要一起),必须包含3,可以包含4。

原文中给了两个例子,分别是opera和safari。其中opera可以将prop设置成window.opera,因为这是Opera独有的属性。

例子:Safari

{
    string: navigator.vendor,
    subString: "Apple",
    identity: "Safari"
}

当脚本检测到这段时,它会查找navigator.verdor的值看它是事包含Apple字符串,如果有,则将BrowserDetect.browser设置成Safari,至此检测结束;如果没有,脚本会继续检测下一个对象。

例子:Opera

{
	prop: window.opera,
	identity: "Opera",
	versionSearch: "Version"
}

在这里,脚本会先检查window.opera属性是否存在,如果存在,则检测结束,设置BrowserDetect.browser为Opera;如果不存在,脚本继续检测下一个对象。

检测顺序

浏览器对象检测的顺序是和它们在dataBrowser的顺序是一样的,这也是为什么dataBrowser采用数组形式的原因。一旦脚本检测成功,它就会结束,而不会再检查接下来的对象。

检测顺序是非常重要的,一般的规则是把受众最少的浏览器(原文是minor browser,应该是指浏览器市场份额最少?)的放在前面。因为这些浏览器往往会为了通过网站的浏览器检测而改变它们的标志。

例如,Opera的userAgent可以包含MSIE,如果我们想要检查IE,当发现userAgent中包含MSIE时,就会不正确地得出结论说这是IE浏览器,而事实上它只是Opera伪装而成的。为了避免错误的检测,我们首先要检测的就是Opera,随后才是IE。另外一对,Safari和Mozilla也有同样的问题。

一般来说,当需要添加新的浏览器到dataBrowser数组中时,往往要放到IE和Mozilla之前。

版本号

一般情况下,浏览器的版本号可以直接从userAgent或者获得, 而像iCab可以从appVersion获得。

改进版本

还有基于ppk的脚本做了几处修改,可以正确显示Opera的版本号以及显示完整的版本号:这里下载