标签:
检测Web客户端的手段很多,而且各有利弊。不到万不得已,就不要使用客户端检测。先设计最通用的方案,然后再使用特定于浏览器的技术增强该方案。
能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力。能力检测的基本模式如下:
if (object.propertyInQuestion) { // 使用 object.propertyInQuestion }
能力检测是检测某个特性是否会按照适当方式行事,而不是仅仅某个特性是否存在。
// 不要这样做!这不是能力检测-只检测了是否存在相应的属性 function isSortable(object) { return !!object.sort; }
更好的方式是检测sort是不是一个函数
// 这样更好:检查是不是函数 function isSortable(object) { return typeof object.sort == "function"; }
注意:宿主对象(DOM对象是一种)没有义务让typeof返回合理的值,特别是在IE8-中,宿主对象是通过COM而非JScript实现的。对函数使用typeof运算符会返回“object”,而不是期待的“function”。还有其它的一些对象有怪异的行为。在浏览器换下可以使用如下函数测试任何对象的某个特性是否存在。
// 作者:Peter Michaux function isHostMethod(object, property) { var t = typeof object[property]; return t == ‘function‘ || (!!(t == ‘object‘ && object[property])) || t == ‘unknown‘; }
根据浏览器不同将能力组合起来时更可取的方式。如果你知道自己的应用程序需要使用某些特定的浏览器特性,那么最好是一次性检测所有相关特性,而不是分别检测。
// 确定浏览器是否支持Netscape风格的插件 var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length); // 确定浏览器是否具有DOM1级规定的能力 var hasDOM1 = !! (document.getElementById && document.createElement && document.getElementByTagName);
得到的布尔值可以在以后继续使用,从而节省重新检测能力的时间。实际开发中,应该将能力检测作为确定下一步解决方案的依据,而不是用来判断用户使用的是什么浏览器。
怪癖检测的目标是识别浏览器的特殊行为。着通常需要运行一小段代码,以确定某一个特性不能正常工作。IE8-中的一个bug是,如果存在某个实例属性与[[Enumerable]]标记为false的某个原型属性同名,那么该实例属性将不会出现在for-in循环中。Safari3-中会枚举被隐藏的属性。
用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。又有历史原型,这种检测是一种无法完美的检测。一般情况下,知道呈现引擎和最低限度的版本就足以决定正确的操作方法了。
主流的五大呈现引擎: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属性。要正确识别呈现引擎,关键是检测顺序要正确,第一步是要识别Opera,代码如下:
if (window.opera) { engine.ver = window.opera.version(); engine.opera = parseFloat(engine.ver); }
第二步要识别WebKit,示例代码如下:
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); }
第三步要识别KHTML,示例代码如下:
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); }
第四部要识别Gecko,示例代码如下:
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); } else if (/rv:([^\)]+)\) Gecko \/\d{8}/.test(ua)) { engine.ver = RegExp["$1"]; engine.gecko = parseFloat(engine.ver); }
最好一步识别IE,示例代码如下:
else if (/MSIE ([^;]+)/.test(ua)) { engine.ver = RegExp["$1"]; engine.ie = parseFloat(engine.ver); }
Safari浏览器和Chrome浏览器都使用WebKit作为呈现引擎,当它们的JavaScript引擎不一样。对于它们,有必要为client对象再添加一些新的属性。代码如下:
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 }; }();
由于大多数浏览器与其呈现引擎密切相关,所有可以混合检测呈现引擎和浏览器。
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 (client.browser.chrome) { // 执行针对Chrome的代码 } else if (client.browser.safari) { // 执行针对Safari的代码 } } else if (client.engine.gecko) { if (client.browser.firefox) { // 执行Firefox的代码 } else { // 执行针对其它Gecko浏览器的代码 } }
具有各种平台版本的浏览器在不同的平台下可能会有不同的问题。为此,还需要给client添加一个新对象。
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 }; }();
确定平台可以检测navigator.platform比检测用户代理字符串更简单。代码如下:
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);
要识别移动设备,client对象进化成下面的样子:
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 }; }();
相应的检测代码是:
system.iphone = ua.indexOf("iPhone") > -1; system.ipod = ua.indexOf("iPod") > -1; system.ipad = ua.indexOf("iPad") > -1; // 检测Android版本 if (/Android (\d+\.\d+)/.test(ua)) { system.android = parseFloat(RegExp.$1); }
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);
用户代理检测是客户端检测的最后一个选择,优先采用能力检测和怪癖检测。用户代理检测一般适用如下情形。
1)不能直接准确地使用能力检测或怪癖检测,因为有些浏览器实现了存根函数。
2)同一款浏览器在不同平台下具备不同的能力。
3)为了跟踪分析等目的需要知道确切的浏览器。
标签:
原文地址:http://www.cnblogs.com/TwoWaterLee/p/5388173.html