《JavaScript高级程序设计》第九章,客户端检测。
浏览器之间差异很多,所以有时候我们不得不针对不同的浏览器做特定的操作,这就需要我们进行客户端检测了。
能力检测
能力检测的目标是识别浏览器的能力,而不是区分特定的浏览器。通常使用if语句来判断是否支持特定方法和功能,最好先判断通用的后判断特殊的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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!"); } }
var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);
var hasDOM = !!(document.getElementById && document.createElement && document.getElementByTagName);
|
需要注意的是,有时候我们不能想当然地认为某个特性会按照适当的方式行事,因此不能只是简单地判断方法存不存在。举个例子:
1 2 3 4 5 6 7 8 9 10
| function isSortable(object) { return !!object.sort; } var result = isSortable({sort: true});
function isSortable(object) { return typeof object.sort == "funtion"; }
|
怪癖检测
怪癖检测(quirks detection)目标是识别浏览器的特殊行为,一般来说怪癖是缺陷。这种检测通常需要运行一小段代码,来确定某一性能不能正常工作。举个例子:
1 2 3 4 5 6 7 8 9 10
| var hasDontEnumQuirk = function() { var o = {toString: function(){}}; for(var prop in o) { if(prop == "toString") { return false; } } return true; }();
|
用户代理检测
用户代理检测是通过用户代理字符串来确定实际使用的浏览器,该字符串是在HTTP请求过程中作为响应首部发送的,可通过JavaScript的navigator.userAgent访问。它的发展历史很长,不同浏览器不同版本也有很多不一致的地方,也存在电子欺骗(spoofing)的问题,可以去翻书看看。这里拿我的浏览器信息举个例子:
1 2
| console.log(nevigator.userAgent);
|
识别呈现引擎
不管是什么浏览器,只要使用同版本的呈现引擎,就会具备相同的功能,因此我们可以在这个方面进行识别区分。主要有五大引擎:IE、Gecko、WebKit、KHTML、Opera。下面是一个检测实现的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var client = function() { var engine = { ie: 0, gecko: 0, webkit: 0, khtml: 0, opera: 0, ver: null }; return { engine: engine }; }();
|
当检测到一个呈现引擎时,将client.engine对应的属性改为一个大于零的值。对于识别过程,我们需要制定检测的顺序,顺序不对则很可能会产生错误的结果。
- 首先我们识别Opera,因为它的用户代理字符串很可能模仿别的浏览器。使用window.opera即可。
- 其次识别WebKit,因为它的字符串中包含”Gecko”和”KHTML”,因此不能先识别它们俩。使用其字符串独有的”AppleWebKit”即可。
- 然后识别KHTML,因为它的字符串中包含”Gecko”,因此不能先识别Gecko。使用其字符串中独有的”Konqueror”即可。
- 然后识别Gecko。
- 最后识别IE。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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); }
|
识别浏览器
只要呈现引擎还不足以说明存在所需的JavaScript功能,比如说Safari浏览器和Chrome浏览器均使用WebKit作为呈现引擎,但二者的JavaScript引擎却不一样。因此我们需要在上面实现的基础上做一些添加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var client = function() { var engine = { ... }; var browser = { ie: 0, firefox: 0, safari: 0, konq: 0, opera: 0, chrome: 0, ver: null };
return { engine: engine, browser: browser }; }();
|
然后就是要确定检测顺序了,不过因为多数浏览器与其呈现引擎密切相关,所以我们会将对浏览器的检测嵌入到对呈现引擎的检测中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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); } else if(/KHTML\/(S+)/.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); } else if(/MSIE ([^;]+)/.test(ua)) { engine.ver = browser.ver = RegExp["$1"]; engine.ie = browser.ie = parseFloat(engine.ver); }
|
识别平台
在某些条件下,我们需要关注平台的差异性。目前的三大主流平台是:Windows、Mac、Unix。同样,我们可以在client对象中添加一个新对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var client = function() { var engine = { ... }; var browser = { ... }; var system = { win: false, mac: false, x11: false }
return { engine: engine, browser: browser, system: system }; }();
|
不过在对平台的识别上,检测navigator.platform要比检测navigator.userAgent更简单,前者有以下几个取值:Win32、Win64、MacPPC、MacIntel、X11、Linux i686。这样我们就可以进行如下的判断:
1 2 3 4
| var p = navigator.platform; system.win = p.indexOf("Win") == 0; system.mac = p.indexOf("Mac") == 0; system.unix = (p.indexOf("X11") == 0) || (p.indexOf("Linux") == 0) ;
|
识别Windows操作系统
即识别Windows的各种版本,不赘述。
识别移动设备
即识别移动设备里的Web浏览器,可在system对象中添加iphone、ipod、ipad、ios、android、nokiaN、winMobile,不赘述。
识别游戏系统
即识别视频游戏系统里的Web浏览器,可在system对象中添加wii、ps,不赘述。