Javascript高级程序设计——客户端检测
ECMAScript虽然是Javascript的核心,但是要在web中使用Javascript,那么BOM才是核心,BOM为我们提供了操作访问浏览器对象的借口,
但是由于BOM没有标准规范,导致存在不一致的问题,所以这里需要客户端检测的方法,突破局限。
先找出设计通用的的方案,然后再使用特定与浏览器的技术增强该方案。
一、能力检测
能力检测的目的不是识别浏览器,而是识别浏览器的能力。不必顾及特定的浏览器,只需确定浏览器支持特定的能力。
//浏览器检测的基本模式 if(object.property){ 使用object.property }
先检测常用的特性,保证了代码最优化。
//上个例子中的代码利用类型转换来确定某个对象是否存在,但这不能保证该成员是不是想要的。 这里更可靠的能力检测 function betterCheck(property){ return (typeof object.property == "function"); }
二、怪癖检测
怪癖检测的目标是识别浏览器的特殊行为。只是检测浏览器的存在缺陷。
//例如 早期IE8中,如果实例属性与[[enumerable]]为false的某个原型同名,那么这个属性遍不会被for-in循环枚举。可以通过下面的代码来检测这个”怪癖“ var hasEnumQuirk = function () { var o = { toString : function () {} }; for (var pro in o ){ if(pro == "toString") return false; } return true; }
怪癖一般都是浏览器所独有的,通常被归为bug,一般仅检测有直接影响的“怪癖”。
三、用户代理检测
在浏览器每次发送的http时,都会将userAgent用户代理字符串作为响应首部发送,可以通过navigator.userAgent访问。
由于经过浏览器大战,现在的浏览器的用户代理都是通过电子欺骗来指定一些误导信息,欺骗服务器。
1、识别呈现引擎
//为了减少全局变量,这里使用增强模块模式。在内部声明了浏览器的呈现引擎,浏览器版本和平台信息 var client = (function () { //呈现引擎 var engine = { ie : 0, gecko : 0, webkit : 0, khtml : 0, opera : 0, ver : null }; //浏览器版本 var broswer = { ie : 0, firefox : 0, chrome : 0, safari : 0, konq : 0, opera : 0, ver : null }; //平台信息 var system = { win : false, mac : false, x11 : false, //移动设备 iphone : false, ipod : false, ipad : false, ios : false, andriod : false, nokia : false, winMobile : false, //游戏平台 will : false, ps : false }; //用户代理检测 var ua = navigator.userAgent; //opera会将自己完全伪造成其他浏览器所以先检测它 if (window.opera){ engine.opera = broswer.opera = parseInt(engine.ver); engine.ver = broswer.ver = window.opera.version(); }else if (/AppleWebKit\/(\S+)/.test(ua)){ //webkit引擎的独有特征是AppleWebKit,利用正则表达式来获取版本号 engine.ver = RegExp.$1; engine.webkit = parseInt(engine.ver); //确认引擎后可以判断浏览器版本,chrome or safari if (/chrome\/(\S+)/.test(ua)){ broswer.ver = RegExp.$1; broswer.chrome = parseFloat(broswer.ver); }else if (/Version\/(\S+)/.test(ua)){ broswer.ver = RegExp.$1; broswer.safari = parseFloat(broswer.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; } broswer.ver = broswer.safari = safariVersion; } } else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/[^;]+/.test(ua)){ engine.ver = broswer.ver = RegExp.$1; engine.konq = broswer.konq = parseFloat(engine.ver); } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){ engine.ver = RegExp.$1; engine.gecko = parseInt(engine.ver); //是否是firefox浏览器 if (/firefox\/(\S+)/i.test(ua)){ broswer.ver = RegExp.$1; broswer.firefox = parseFloat(broswer.ver); } //如果这里没有检测到的firefox岂不是没有浏览器的版本信息了? } else if (/MSIE ([^;]+)/.test(ua)){ engine.ver = broswer.ver = RegExp.$1; engine.ie = broswer.ie = parseFloat(engine.ver); } //检测浏览器,不已经检测过了吗? broswer.ie = engine.ie; broswer.opera = engine.opera; //检测平台的开始自动判断平台 var p = navigator.platform; system.win = p.indexOf("Win") == 0; system.mac = p.indexOf("Mac") == 0; system.xll = (p == "X11" || p.indexOf("Linux") == 0); 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 = "Visita"; 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.ipod = ua.indexOf("ipad") > -1; system.nokia = ua.indexOf("NokiaN") > -1; //windowMobile if (system.win == "CE"){ system.winMobilem = system.win; } else if (system.win == "Ph"){ if(/Windows Phone OS (\d+.\d+)/.test(ua)){ system.win = "Phone"; system.winMobilem = parseFloat(RegExp.$1); } } //ios if (system.mac $$ ua.indexOf("Mobile") > -1){ if (/CPU (?:iPhone ) ?OS (\d+.\d+)/.test(ua)){ system.iso = parseFloat(RegExp.$1.replace("_", ".")); } else { system.iso = 2; }) } //检测安卓 if (/Andriod (\d+ \. \d+)/.test(ua)){ system.andriod = parseFloat(RegExp.$1); } system.will = ua.indexOf("Wii") > -1; system.ps = /PLAYSTATION/i.test(ua); return { engine : engine, broswer : broswer, system : system }; })();
浏览器检测只是最后一中选择,只要有可能就应该优先采用能力检测和怪癖检测。适用以下三种情形:
1、不能直接准确的使用能力检测和怪癖检测
2、浏览器在不同平台下不同的能力,需要检测平台。
3、跟踪分析等目的需要知道确切的浏览器。
小结:
客户端检测时有争议的,由于浏览器之间存在差别,通常需要根据不同浏览器,写不同的代码。客户端检测的常用方法:
- 能力检测,在编写代码前,先检测浏览器的能力,把注意集中在浏览器的能力存在上。
- 怪癖检测,浏览器中的bug的检测。
- 用户代理检测,根据用户代理字符串来识别浏览器。
一般,优先考虑能力检测,怪癖检测时第二选择,引文用户代理检测对用户代理字符串的依赖非常强大。