js高级程序设计(九)客户端检测

能力检测

能力检测又称特性检测,能力检测的目标不是 识别特定的浏览器,而是识别浏览器的能力。

举个例子,IE 的早期版本中不存在 document.getElementById()

function getElement(id){
   if (document.getElementById){
     return document.getElementById(id);
   } else if (document.all){
     return document.all[id]; 
   } else {
     throw new Error("No way to retrieve element!");
   }
} 

先检测达成目的的最常用的特性。这样可以保证代码最优化,因为在多数情况下都可以避免测试多个条件。

必须测试实际要用到的特性。一个特性存在,不一定意味着另一个特性也存在。

function getWindowWidth(){
   if (document.all){ //假设是 IE
     return document.documentElement.clientWidth; //错误的用法!!!
   } else {
     return window.innerWidth;
   }
} 

更可靠的能力测试

能力检测就是用来判断某个特性是否会按照适当方式行事。

//不要这样做!这不是能力检测——只检测了是否存在相应的方法或者属性
function isSortable(object){
     return !!object.sort;
} 

//这样也会返回true
var result = isSortable({ sort: true }); 

//这样更好:检查 sort 是不是函数
function isSortable(object){
     return typeof object.sort == "function";
} 

在可能的情况下,要尽量使用 typeof 进行能力检测。大多数浏览器在检测到 document.createElement() 存在时,都会返回 true。但是在 IE8 及之前版本中不行。IE8中typeof document.createElement 返回的是 "object",而不是"function"。IE 及更早版本中的宿主对象是通 过 COM 而非 JScript 实现的。因此,document.createElement()函数确实是一个 COM 对象,所以 typeof 才会返回"object"。IE9 纠正了这个问题,对所有 DOM 方法都返回"function"。

关于 typeof 的行为不标准,IE 中还可以举出例子来。ActiveX 对象(只有 IE 支持)与其他对象的行 为差异很大。

IE 对 typeof xhr.open 会返回"unknown"。所以要使用以下函数来确定任何对象的某个属性是否存在。

function isHostMethod(object, property) {
   var t = typeof object[property];
   return t=='function' ||
       (!!(t=='object' && object[property])) ||
       t=='unknown';
} 

能力检测,不是浏览器检测

检测某个或某几个特性并不能够确定浏览器。

//错误!还不够具体
var isFirefox = !!(navigator.vendor && navigator.vendorSub);
//错误!假设过头了
var isIE = !!(document.all && document.uniqueID); 

怪癖检测

怪癖检测(quirks detection)的目标是识别浏览器的特殊行为。知道浏览器存在什么缺陷。

用户代理检测

用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。在每一次 HTTP 请求过程中,用户代理字符串是作为响应首部发送的, 而且该字符串可以通过 JavaScript 的 navigator.userAgent 属性访问。

用户代理字符串的历史

早期浏览器

1993 年,美国 NCSA发布 了世界上第一款 Web 浏览器 Mosaic。这款浏览器的用户代理字符串非常简单。

Mosaic/0.9 

Netscape Communications 公司介入浏览器开发领域后,遂将自己产品的代号定名为 Mozilla(Mosaic Killer 的简写,意即 Mosaic 杀手)。该公司第一个公开发行版,Netscape Navigator 2 的用户代理字符串 具有如下格式。

Mozilla/2.02 [fr] (WinNT; I) 

它包含了语言,平台和加密类型。

Netscape Navigator 3和Internet Explorer 3

运行在 Windows 系统下的 Netscape Navigator 3 的用户代理字符串

Mozilla/3.0 (Win95; U) 

Netscape Navigator 3 发布后不久,微软也发布了其第一款赢得用户广泛认可的 Web 浏览器,即 Internet Explorer 3。由于 Netscape 浏览器在当时占绝对市场份额,许多服务器在提供网页之前都要专门 检测该浏览器。如果用户通过 IE 打不开相关网页,那么这个新生的浏览器很可能就会夭折。于是,微 软决定将 IE 的用户代理字符串修改成兼容 Netscape 的形式。

Mozilla/2.0 (compatible; MSIE 3.02; Windows 95) 

由于当时的大多数浏览器嗅探程序只检测用户代理字符串中的产品名称部分,结果 IE 就成功地将 自己标识为 Mozilla,从而伪装成 Netscape Navigator。微软的这一做法招致了很多批评,因为它违反了 浏览器标识的惯例。更不规范的是,IE 将真正的浏览器版本号插入到了字符串的中间。

Netscapte Communicator 4 和 IE4~IE8

Netscapte Communicator 4

Mozilla/4.0 (Win98; I)

微软 Internet Explorer 4

Mozilla/4.0 (compatible; MSIE 4.0; Windows 98) 

在 Internet Explorer 4.5 发布时(只针对 Macs), 虽然 Mozilla 版本号还是 4,但 IE 版本号则改成了如下所示:

Mozilla/4.0 (compatible; MSIE 4.5; Mac_PowerPC) 

IE 的版本一直到 7 都沿袭了这个模式:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1) 

而 IE8 的用户代理字符串中添加了呈现引擎(Trident)的版本号:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)

这个新增的 Trident 记号是为了让开发人员知道 IE8 是不是在兼容模式下运行。如果是,则 MSIE 的 版本号会变成 7,但 Trident 及版本号还会留在用户代码字符串中:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0) 

IE9 对字符串格式做了一点调整。Mozilla 版本号增加到了 5.0,而 Trident 的版本号也升到了 5.0。IE9 默认的用户代理字符串如下:

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) 

如果 IE9 运行在兼容模式下,字符串中的 Mozilla 版本号和 MSIE 版本号会恢复旧的值,但 Trident 的版本号仍然是 5.0。例如,下面就是 IE9 运行在 IE7 兼容模式下的用户代理字符串:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0) 

Gecko

Gecko 是 Firefox 的呈现引擎。当初的 Gecko 是作为通用 Mozilla 浏览器的一部分开发的,而第一个 采用 Gecko 引擎的浏览器是 Netscape 6。以下是基于Gecko内核的用户代理字符串。

Mozilla/Mozilla 版本号 (平台; 加密类型; 操作系统或 CPU; 语言; 预先发行版本)
 Gecko/Gecko 版本号 应用程序或产品/应用程序或产品版本号

Windows XP 下的 Netscape 6.21:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4) Gecko/20011128 Netscape6/6.2.1
Linux 下的 SeaMonkey 1.1a:
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1b2) Gecko/20060823 SeaMonkey/1.1a
Windows XP 下的 Firefox 2.0.0.11:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11
Mac OS X 下的 Camino 1.5.1:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.6) Gecko/20070809 Camino/1.5.1

随着 Firefox 4 发布,Mozilla 简化了这个用户代理字符串。

Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox 4.0.1 

WebKit

2003 年,Apple 公司宣布要发布自己的 Web 浏览器,名字定为 Safari。Safari 的呈现引擎叫 WebKit, 是 Linux 平台中 Konqueror 浏览器的呈现引擎 KHTML 的一个分支。几年后,WebKit 独立出来成为了一 个开源项目,专注于呈现引擎的开发。 这款新浏览器和呈现引擎的开发人员也遇到了与 Internet Explorer 3.0 类似的问题:如何确保这款浏 览器不被流行的站点拒之门外?答案就是向用户代理字符串中放入足够多的信息,以便站点能够信任它 与其他流行的浏览器是兼容的。

Mozilla/5.0 (平台; 加密类型; 操作系统或 CPU; 语言) AppleWebKit/AppleWebKit 版本号(KHTML, like Gecko) Safari/Safari 版本号

Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124 (KHTML, like Gecko) Safari/125.1 

Safari 预发行 1.0 版用户代理字符串中最耐人寻味,也是最饱受诟病的部分就是字符串"(KHTML, like Gecko)"。Apple 因此收到许多开发人员的反馈,他们认为这个字符串明显是在欺骗客户端和服务器,实际上是想让它们把 Safari 当成 Gecko(好像光添加 Mozilla/5.0 还嫌不够)。Apple 的回应与 微软在 IE 的用户代理字符串遭到责难时如出一辙:Safari 与 Mozilla 兼容,因此网站不应该将 Safari 用 户拒之门外,否则用户就会认为自己的浏览器不受支持。

到了 Safari 3.0 发布时,其用户代理字符串又稍微变长了一点。下面这个新增的 Version 记号一直到 现在都被用来标识 Safari 实际的版本号:

Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5

这个变化只在 Safari 中有,在 WebKit 中没有。

Konqueror

Konqueror 3.2

Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.0 (like Gecko) 

Chrome

谷歌公司的 Chrome 浏览器以 WebKit 作为呈现引擎,但使用了不同的 JavaScript 引擎。在 Chrome 0.2 这个最初的 beta 版中,用户代理字符串完全取自 WebKit,只添加了一段表示 Chrome 版本号的信息,格 式如下:

Chrome 7 的完整的用户代理字符串

Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7 

Opera

在 Opera 8.0 之前,其用户代理字符 串采用如下格式:

Opera/7.54 (Windows NT 5.1; U) [en] 

Opera 8 发布后,用户代理字符串的“语言”部分被移到圆括号内

Opera/8.0 (Windows NT 5.1; U; en) 

Opera 9 以后,出现了两种修改用户代理字符串的方式。一种方式是将自身标识为另外一个浏览器, 如 Firefox 或者 IE。在这种方式下,用户代理字符串就如同 Firefox 或 IE 的用户代理字符串一样,只不 过末尾追加了字符串 Opera 及 Opera 的版本号。

Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50 

Opera 10 对代理字符串进行了修改。

Opera/9.80 (Windows NT 6.1; U; en) Presto/2.6.30 Version/10.63 

IOS和Android

移动操作系统 iOS 和 Android 默认的浏览器都基于 WebKit,而且都像它们的桌面版一样,共享相同 的基本用户代理字符串格式。

Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16 

Android 浏览器中的默认格式与 iOS 的格式相似

Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 

用户代理字符串检测技术

一般情况下, 知道呈现引擎和最低限度的版本就足以决定正确的操作方法了。但是不推荐以下方法:

if (isIE6 || isIE7) { //不推荐!!!
 //代码
}

这种代码其实是很脆弱的,因为它要依据特定的版本来决定做什么。如果是 IE8 怎么办呢?只要 IE 有新版本出来,就必须更新这些代码。因此,我们用以下方法:

if (ieVer >=6){
 //代码
}

1.识别呈现引擎

确切知道浏览器的名字和版本号不如确切知道它使用的是什么呈现引擎。如果 Firefox、 Camino 和 Netscape 都使用相同版本的 Gecko,那它们一定支持相同的特性。因此,我们要编写的脚本将主要检测五大呈现引擎:IE、Gecko、WebKit、KHTML 和 Opera

为了不在全局作用域中添加多余的变量,我们将使用模块增强模式来封装检测脚本。

var client = function(){

 var engine = {
 //呈现引擎
 ie: 0,
 gecko: 0,
 webkit: 0,
 khtml: 0,
 opera: 0,

 //具体的版本号
 ver: null
 };
 //在此检测呈现引擎、平台和设备

 return {
 engine : engine
 };
}();

这里声明了一个名为 client 的全局变量,用于保存相关信息。匿名函数内部定义了一个局部变量 engine,它是一个包含默认设置的对象字面量。在这个对象字面量中,每个呈现引擎都对应着一个属 性,属性的值默认为 0。如果检测到了哪个呈现引擎,那么就以浮点数值形式将该引擎的版本号写入相 应的属性。而呈现引擎的完整版本(是一个字符串),则被写入 ver 属性。

作这样的区分可以支持像下面这样编写代码:

if (client.engine.ie) { //如果是 IE,client.ie 的值应该大于 0
 //针对 IE 的代码
} else if (client.engine.gecko > 1.5){
 if (client.engine.ver == "1.8.1"){
 //针对这个版本执行某些操作
 }
} 

要正确地识别呈现引擎,关键是检测顺序要正确。第一步就是识别 Opera,因为它的用户代理字符串有可能完全模仿其他浏览器。

要识别 Opera,必须得检测 window.opera 对象。Opera 5 及更高版本中都有这个对象,用以保存 与浏览器相关的标识信息以及与浏览器直接交互。在 Opera 7.6 及更高版本中,调用 version()方法可 以返回一个表示浏览器版本的字符串,而这也是确定Opera版本号的最佳方式。要检测更早版本的Opera, 可以直接检查用户代理字符串,因为那些版本还不支持隐瞒身份。不过,2007 底 Opera 的最高版本已经 是 9.5 了,所以不太可能有人还在使用 7.6 之前的版本。

if (window.opera){
 engine.ver = window.opera.version();
 engine.opera = parseFloat(engine.ver);
} 

将版本的字符串表示保存在了 engine.ver 中,将浮点数值表示的版本保存在了 engine.opera 中。如果浏览器是 Opera,测试 window.opera 就会返回 true。

应该放在第二位检测的呈现引擎是 WebKit。WebKit 的用户代理字符串中的"AppleWebKit"是独一无二的。

var ua = navigator.userAgent;
if (window.opera){
 engine.ver = window.opera.version();
 engine.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.webkit = parseFloat(engine.ver);
} 

代码首先将用户代理字符串保存在变量 ua 中。然后通过正则表达式来测试其中是否包含字符串 "AppleWebKit",并使用捕获组来取得版本号。由于实际的版本号中可能会包含数字、小数点和字母, 所以捕获组中使用了表示非空格的特殊字符(\S)。用户代理字符串中的版本号与下一部分的分隔符是 一个空格,因此这个模式可以保证捕获所有版本信息。test()方法基于用户代理字符串运行正则表达 式。如果返回 true,就将捕获的版本号保存在 engine.ver 中,而将版本号的浮点表示保存在 engine.webkit 中。

接下来要测试的呈现引擎是 KHTML。KHTML 的版本号与 WebKit 的版本号 在用户代理字符串中的格式差不多,因此可以使用类似的正则表达式。此外,由于 Konqueror 3.1 及更 早版本中不包含 KHTML 的版本,故而就要使用 Konqueror 的版本来代替。

var ua = navigator.userAgent;
if (window.opera){
 engine.ver = window.opera.version();
 engine.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.webkit = parseFloat(engine.ver);
} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.khtml = parseFloat(engine.ver);
} 

在排除了 WebKit 和 KHTML 之后,就可以准确地检测 Gecko 了。但是,在用户代理字符串中,Gecko 的版本号不会出现在字符串"Gecko"的后面,而是会出现在字符串"rv:"的后面。

var ua = navigator.userAgent;
if (window.opera){
 engine.ver = window.opera.version();
 engine.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.webkit = parseFloat(engine.ver);
} else if (/KHTML\/(\S+)/.test(ua)) {
 engine.ver = RegExp["$1"];
 engine.khtml = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.gecko = parseFloat(engine.ver);
}

Gecko 的版本号位于字符串"rv:"与一个闭括号之间,因此为了提取出这个版本号,正则表达式要 查找所有不是闭括号的字符,还要查找字符串"Gecko/"后跟 8 个数字。如果上述模式匹配,就提取出 版本号并将其保存在相应的属性中。

最后一个要检测的呈现引擎就是 IE 了。IE 的版本号位于字符串"MSIE"的后面、一个分号的前面, 因此相应的正则表达式非常简单。

var ua = navigator.userAgent;
if (window.opera){
 engine.ver = window.opera.version();
 engine.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.webkit = parseFloat(engine.ver);
} else if (/KHTML\/(\S+)/.test(ua)) {
 engine.ver = RegExp["$1"];
 engine.khtml = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.gecko = parseFloat(engine.ver);
} else if (/MSIE ([^;]+)/.test(ua)){
 engine.ver = RegExp["$1"];
 engine.ie = parseFloat(engine.ver);
}

2.识别浏览器

只有呈 现引擎还不能说明存在所需的 JavaScript 功能。苹果公司的 Safari 浏览器和谷歌公司的 Chrome 浏览器都 使用 WebKit 作为呈现引擎,但它们的 JavaScript 引擎却不一样。

var client = function(){
 var engine = { 
  //呈现引擎
   ie: 0,
   gecko: 0,
   webkit: 0,
   khtml: 0,
   opera: 0,

   //具体的版本
   ver: null
 };
var browser = {    //浏览器   ie: 0,   firefox: 0,   safari: 0,   konq: 0,   opera: 0,   chrome: 0,   //具体的版本   ver: null }; //在此检测呈现引擎、平台和设备 return {   engine: engine,   browser: browser }; }();

代码中又添加了私有变量 browser,用于保存每个主要浏览器的属性。

//检测呈现引擎及浏览器
var ua = navigator.userAgent;
if (window.opera){
   engine.ver = browser.ver = window.opera.version();
   engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)){
   engine.ver = RegExp["$1"];
   engine.webkit = parseFloat(engine.ver);

   //确定是 Chrome 还是 Safari
   if (/Chrome\/(\S+)/.test(ua)){
     browser.ver = RegExp["$1"];
     browser.chrome = parseFloat(browser.ver);
   } else if (/Version\/(\S+)/.test(ua)){
     browser.ver = RegExp["$1"];
     browser.safari = parseFloat(browser.ver);
   } else {
     //近似地确定版本号
     var safariVersion = 1; 
    if (engine.webkit < 100){
       safariVersion = 1;
     } else if (engine.webkit < 312){
       safariVersion = 1.2;
     } else if (engine.webkit < 412){
       safariVersion = 1.3;
     } else {
       safariVersion = 2;
     }

     browser.safari = browser.ver = safariVersion;
   }
} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)){
   engine.ver = browser.ver = RegExp["$1"];
   engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){
   engine.ver = RegExp["$1"];
   engine.gecko = parseFloat(engine.ver);

   //确定是不是 Firefox
   if (/Firefox\/(\S+)/.test(ua)){
     browser.ver = RegExp["$1"];
     browser.firefox = parseFloat(browser.ver);
   }
} else if (/MSIE ([^;]+)/.test(ua)){
   engine.ver = browser.ver = RegExp["$1"];
   engine.ie = browser.ie = parseFloat(engine.ver);
} 

有了上面这些代码之后,我们就可以编写下面的逻辑。

if (client.engine.webkit) { //if it’s WebKit
   if (client.browser.chrome){
   //执行针对 Chrome 的代码
   } else if (client.browser.safari){
   //执行针对 Safari 的代码
   }
} else if (client.engine.gecko){
   if (client.browser.firefox){
   //执行针对 Firefox 的代码
   } else {
   //执行针对其他 Gecko 浏览器的代码
   }
} 

3.识别平台

var client = function(){
   var engine = {
     //呈现引擎
     ie: 0,
     gecko: 0,
     webkit: 0,
     khtml: 0,
     opera: 0,
     //具体的版本号
     ver: null
   };
   var browser = {
     //浏览器
     ie: 0,
     firefox: 0,
     safari: 0,
     konq: 0,
     opera: 0,
     chrome: 0,
     //具体的版本号
     ver: null
   };
   var system = {
     win: false,
     mac: false,
     x11: false
   };
   //在此检测呈现引擎、平台和设备
   return {
     engine: engine,
     browser: browser,
     system: system
   };
}(); 

var p = navigator.platform;
system.win = p.indexOf("Win") == 0;
system.mac = p.indexOf("Mac") == 0;
system.x11 = (p.indexOf("X11") == 0) || (p.indexOf("Linux") == 0);

4.识别windows操作系统

第一步就是匹配 Windows 95 和 Windows 98 这两个字符串。

/Win(?:dows )?([^do]{2})/ 

Gecko 在表示 Windows NT 时会在末尾添加"4.0",不如像下面这样查找小数值更合适。

/Win(?:dows )?([^do]{2})(\d+\.\d+)?/ 

正则表达式中就包含了第二个捕获组,用于取得 NT 的版本号。由于该版本号对于 Windows 95 和 Windows 98 而言是不存在的,所以必须设置为可选。

/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/ 

判断windows版本的代码如下:

if (system.win){
   if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)){
     if (RegExp["$1"] == "NT"){
       switch(RegExp["$2"]){
         case "5.0":
           system.win = "2000";
           break;
         case "5.1":
           system.win = "XP";
           break;
         case "6.0":
           system.win = "Vista";
           break;
         case "6.1":
           system.win = "7";
           break;
         default:
           system.win = "NT";
           break;
       }
     } else if (RegExp["$1"] == "9x"){
       system.win = "ME";
     } else {
       system.win = RegExp["$1"];
     }
   }
} 

有了这些检测平台的代码后, 我们就可以编写如下代码。

if (client.system.win){
   if (client.system.win == "XP") {
     //说明是 XP
   } else if (client.system.win == "Vista"){
     //说明是 Vista
   }
} 

5.识别移动设备

var client = function(){
   var engine = {
     //呈现引擎
     ie: 0,
     gecko: 0,
     webkit: 0,
     khtml: 0,
     opera: 0,
     //具体的版本号
     ver: null
   };
   var browser = {
     //浏览器
     ie: 0,
     firefox: 0,
     safari: 0,
     konq: 0,
     opera: 0,
     chrome: 0,
     //具体的版本号
     ver: null
 };
   var system = {
     win: false,
     mac: false, 
     x11: false,
     //移动设备
     iphone: false,
     ipod: false,
     ipad: false,
     ios: false,
     android: false,
     nokiaN: false,
     winMobile: false };
     //在此检测呈现引擎、平台和设备
   return {
     engine: engine,
     browser: browser,
     system: system
   };
}(); 

简单地检测字符串"iPhone"、"iPod"和"iPad",就可以分别设置相应属性的值了。

system.iphone = ua.indexOf("iPhone") > -1;
system.ipod = ua.indexOf("iPod") > -1;
system.ipod = ua.indexOf("iPad") > -1;

除了知道iOS 设备,最好还能知道iOS 的版本号。在iOS 3 之前,用户代理字符串中只包含"CPU like Mac OS",后来iPhone中有改成"CPU iPhone OS 3_0 like Mac OS X",iPad 中又改成"CPU OS 3_2 like Mac OS X"。

//检测iOS 版本
if (system.mac && ua.indexOf("Mobile") > -1){
  if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)){
    system.ios = parseFloat(RegExp.$1.replace("_", "."));
  } else {
    system.ios = 2; //不能真正检测出来,所以只能猜测
  }
}

检查系统是不是Mac OS、字符串中是否存在"Mobile",可以保证无论是什么版本,system.ios中都不会是0。然后,再使用正则表达式确定是否存在iOS 的版本号。如果有,将system.ios 设置为
表示版本号的浮点值;否则,将版本设置为2。

检测Android 操作系统也很简单,也就是搜索字符串"Android"并取得紧随其后的版本号。

//检测Android 版本
if (/Android (\d+\.\d+)/.test(ua)){
  system.android = parseFloat(RegExp.$1);
}

用户代理字符串中是否存在"NokiaN",就足以确定是不是该系列的手机了。

system.nokiaN = ua.indexOf("NokiaN") > -1;

在了解这些设备信息的基础上,就可以通过下列代码来确定用户使用的是什么设备中的WebKit 来访问网页:

if (client.engine.webkit){
  if (client.system. iOS){
    //iOS 手机的内容
  } else if (client.system.android){
    //Android 手机的内容
  } else if (client.system.nokiaN){
    //诺基亚手机的内容
  }
}

6.识别游戏系统

Opera/9.10 (Nintendo Wii;U; ; 1621; en)
Mozilla/5.0 (PLAYSTATION 3; 2.00)

 system.wii = ua.indexOf("Wii") > -1;
 system.ps = /playstation/i.test(ua);

完整的代码

var client = function(){
  //呈现引擎
  var engine = {
    ie: 0,
    gecko: 0,
    webkit: 0,
    khtml: 0,
    opera: 0,
    //完整的版本号
    ver: null
  };
  //浏览器
  var browser = {
    //主要浏览器
    ie: 0,
    firefox: 0,
    safari: 0,
    konq: 0,
    opera: 0,
    chrome: 0,
    //具体的版本号
    ver: null
  };
  //平台、设备和操作系统
  var system = {
    win: false,
    mac: false,
    x11: false,
    //移动设备
    iphone: false,
    ipod: false,
    ipad: false,
    ios: false,
    android: false,
    nokiaN: false,
    winMobile: false,
    //游戏系统
    wii: false,
    ps: false
  };
  //检测呈现引擎和浏览器
  var ua = navigator.userAgent;
    if (window.opera){
      engine.ver = browser.ver = window.opera.version();
      engine.opera = browser.opera = parseFloat(engine.ver);
    } else if (/AppleWebKit\/(\S+)/.test(ua)){
      engine.ver = RegExp["$1"];
      engine.webkit = parseFloat(engine.ver);
      //确定是Chrome 还是Safari
      if (/Chrome\/(\S+)/.test(ua)){
        browser.ver = RegExp["$1"];
        browser.chrome = parseFloat(browser.ver);
      } else if (/Version\/(\S+)/.test(ua)){
        browser.ver = RegExp["$1"];
        browser.safari = parseFloat(browser.ver);
      } else {
        //近似地确定版本号
        var safariVersion = 1;
        if (engine.webkit < 100){
          safariVersion = 1;
        } else if (engine.webkit < 312){
          safariVersion = 1.2;
        } else if (engine.webkit < 412){
          safariVersion = 1.3;
        } else {
          safariVersion = 2;
        }
        browser.safari = browser.ver = safariVersion;
      }
    } else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)){
      engine.ver = browser.ver = RegExp["$1"];
      engine.khtml = browser.konq = parseFloat(engine.ver);
     } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){
      engine.ver = RegExp["$1"];
      engine.gecko = parseFloat(engine.ver);
      //确定是不是Firefox
      if (/Firefox\/(\S+)/.test(ua)){
        browser.ver = RegExp["$1"];
        browser.firefox = parseFloat(browser.ver);
      }
    } else if (/MSIE ([^;]+)/.test(ua)){
      engine.ver = browser.ver = RegExp["$1"];
      engine.ie = browser.ie = parseFloat(engine.ver);
    }
  //检测浏览器
  browser.ie = engine.ie;
  browser.opera = engine.opera;
  //检测平台
  var p = navigator.platform;
  system.win = p.indexOf("Win") == 0;
  system.mac = p.indexOf("Mac") == 0;
  system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);
  //检测Windows 操作系统
  if (system.win){
    if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)){
      if (RegExp["$1"] == "NT"){
        switch(RegExp["$2"]){
          case "5.0":
            system.win = "2000";
            break;
          case "5.1":
            system.win = "XP";
            break;
          case "6.0":
            system.win = "Vista";
            break;
          case "6.1":
            system.win = "7";
            break;
          default:
            system.win = "NT";
            break;
        }
      } else if (RegExp["$1"] == "9x"){
        system.win = "ME";
      } else {
        system.win = RegExp["$1"];
      }
    }
  }
  //移动设备
  system.iphone = ua.indexOf("iPhone") > -1;
  system.ipod = ua.indexOf("iPod") > -1;
  system.ipad = ua.indexOf("iPad") > -1;
  system.nokiaN = ua.indexOf("NokiaN") > -1;
  //windows mobile
  if (system.win == "CE"){
    system.winMobile = system.win;
  } else if (system.win == "Ph"){
    if(/Windows Phone OS (\d+.\d+)/.test(ua)){;
      system.win = "Phone";
      system.winMobile = parseFloat(RegExp["$1"]);
    }
  }
  //检测iOS 版本
  if (system.mac && ua.indexOf("Mobile") > -1){
    if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)){
      system.ios = parseFloat(RegExp.$1.replace("_", "."));
    } else {
      system.ios = 2; //不能真正检测出来,所以只能猜测
    }
  }
  //检测Android 版本
  if (/Android (\d+\.\d+)/.test(ua)){
    system.android = parseFloat(RegExp.$1);
  }
  //游戏系统
  system.wii = ua.indexOf("Wii") > -1;
  system.ps = /playstation/i.test(ua);
  //返回这些对象
  return {
    engine: engine,
    browser: browser,
    system: system
  };
}();

 

posted @ 2017-11-05 23:29  hahazexia  阅读(293)  评论(0)    收藏  举报