Chapter 9 客户端检测
能力检测
-
用于识别浏览器的能力,基本模式如下
if (object.propertyInQuestion) { // use object.propertyInQuestion }
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'); } }
-
更可靠的能力检测
- 确定一个对象是否支持排序(既要检测 sort 属性是否存在,也要检测 sort 属性是否是一个函数)
function isSortable(object) { return typeof object.sort == 'function'; }
- 尽量使用 typeof 进行能力检测。但是在 IE8 之前的版本中,由于宿主对象是通过 COM 而非 JScript 实现的,所以 document.createElement() 之类的函数是一个 COM 对象,而非函数
typeof document.createElement == 'function' // 在 IE8- 中为 false
- IE 中类似的 typeof 的行为不标准的例子还有很多。
- 在浏览器环境下测试任何对象的某个属性是否存在,要使用下面这个函数
function isHostMethod(object, property) { var t = typeof object[property]; return t == 'function' || (!!(t == 'object' && object[property])) || t == 'unknown;
result = isHostMethod(xhr, 'open'); // true
在 JavaScript 中, !! 操作符通常用于将表达式强制转换为 boolean 类型数据。
-
能力检测,不是浏览器检测
- 检测某个或某几个特性并不能够确定浏览器。
- 如果已知应用程序需要使用某些特定的浏览器特性,最好一次检测所有的相关特性,而不是分别检测。
// 检测浏览器是否具有 DOM1 级规定的能力。 var hasDOM1 = !! (document.getElementById && document.createElement && document.getElementByTagName);
怪癖检测
怪癖检测的目标是识别浏览器的特殊行为。通过怪癖检测,获知浏览器存在什么样的缺陷。
-
Example: IE8- 中,如果某个实例属性与 [[Enumerable]] 标记为 false 的某个原型属性同名,那么该实例属性将不会出现在 for-in 循环当中。
var hasDontEnumQuirk = function() { var o = {toString: function() { for(var prop in o) { if(prop == 'toString') { return false; } } } return true; }();
Example: Safari3- 中,会枚举被隐藏的属性。
var hasEnumShadowsQuirk() {
var o = {toString: function() {
var count = 0;
for (var prop in o) {
if (prop == 'toString') {
count++;
}
}
return (count > 1);
}();
3. Example: Safari3- 中,会枚举被隐藏的属性。
```
var hasEnumShadowsQuirk() {
var o = {toString: function() {
var count = 0;
for (var prop in o) {
if (prop == 'toString') {
count++;
}
}
return (count > 1);
}();
```
用户代理检测
-
用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。在每一次 HTTP 请求过程中,用户代理字符串是作为相应首部发送的,而且该字符串可以通过 JavaScript 的 navigator.userAgent 属性访问。
- 在服务器端,用户代理检测被广泛使用。
- 在客户端,用户代理检测的优先级排在能力检测 / 怪癖检测之后。
电子欺骗:浏览器通过在自己的用户代理字符串加入一些错误或误导信息,来达到欺骗服务器的目的。
-
用户代理字符串
- RFC2616 规定格式:标识符/产品版本号
- IE4 ~ IE7:Mozilla/4.0 (平台; MSIE 版本号; 操作系统)
- IE8:Mozilla/4.0 (compatible; MSIE 版本号; Trident/Trident 版本号)
- Trident 记号是为了让开发人员知道 IE8 是不是在兼容模式下运行,如果是,则 MSIE 版本号会变成 7。
- IE9:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
- IE9 兼容模式:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0)
- Gecko:Mozilla/Mozilla 版本号 (平台; 加密类型; 操作系统或CPU; 语言; 预先发型版本) Gecko/Gecko 版本号 应用程序或产品/引用程序或产品版本号
- 具体查手册
- Firefox4: Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox 4.0.1
- Webkit:Mozilla/5.0 (平台; 加密类型; 操作系统或CPU; 语言) AppleWebkit/AppleWebkit 版本号 (KHTML, like Gecko) Safari/Safari 版本号
- 基于 Webkit 的所有浏览器都将自己标识为 Mozilla 5.0
- Safari3: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
- Konqueror 略
- Chrome7: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
- Opera8:Opera/版本号 (操作系统或CPU; 加密类型; 语言)
- Opera9:Mozilla/5.0 (Windows NT 5.1; U; en-US; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50
- Opera隐藏了自己的身份,伪装成 Firefox 或 IE,无法将 Opera 与其他浏览器区别开来。
- Opera10:Opera/9.80 (操作系统或CPU; 加密类型; 语言) Presto/Presto 版本号 Version/版本号
- iOS 和 Android:同桌面版
-
用户代理字符串检测技术
-
识别呈现引擎
- 检测五大引擎: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 }; }();
- 如果检测到了哪个呈现引擎,就以浮点数值形式将该引擎的版本号写入相应的属性。而呈现引擎的完整版本(是一个字符串),则被写入 ver 属性。
if(client.engine.ie) { // 针对 IE 的代码 } else if (client.engine.gecko > 1.5) { if (client.engine.ver == '1.8.1') { // 针对这个版本执行某些代码 } }
- 识别 Opera 必须检测 window.opera 对象。
if (window.opera) { engine.var = window.opera.version(); engine.opera = parseFloat(engine.ver);
- 识别 WebKit 和 KHTML 见手册。
识别浏览器(具体见文档)
var browser = { ie: 0, firefox: 0, safari: 0, konq: 0, opera: 0, chrome: 0, ver: null };
- 识别平台(具体见文档)
var system = { win: false, mac: false, x11: false }; 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);
- 识别移动设备(具体见文档)
var system = { win: false, mac: false, x11: false, iphone: false, ipod: false, ipad: false, ios: false, android: false, nokiaN: false, winMobile: false }; 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); system.iphone = ua.indexOf('iPhone') > -1; system.ipod = ua.indexOf('iPod') > -1; system.ipad = ua.indexOf('iPad') > -1; system.nokiaN = ua.indexOf('NokiaN') > -1; system.winMobile = (system.win == 'CE');
- 识别游戏系统(略)
-
完整的代码(看文档)