客户端检测

客户端检测是javascript开发中最具争议的一个话题。由于浏览器间存在的差别,通常需要根据不同浏览器的能力分别编写不同的代码。有不少客户端检测方法,但下列是最经常实用的。

1)能力检测:在编写代码之前先检测特定浏览器的能力。例如,脚本在调用某个函数之前,可能要先检测该函数是否存在。这种检测方法将开发人员从考虑具体的浏览器类型和版本中解放出来,让他们把注意力集中到相应的能力是否存在上。能力检测无法精确地检测特定的浏览器和版本。

2)怪癖检测:怪癖实际上是浏览器实现中存在的bug,例如早起的WebKit中就存在一个怪癖,即它会在for-in循环中返回被隐蔽的属性。怪癖检测通常涉及到运行的一小段代码,然后确定浏览器是否存在某个怪癖。由于怪癖检测与能力检测相比效率更低,因此应该只在某个怪癖会干扰脚本运行的情况下使用。怪癖检测无法精确地检测特定的浏览器和版本。

3)用户代理检测:通过检测用户代理字符串来识别浏览器。用户代理字符串中包含大量与浏览器有关的信息,包括浏览器、平台、操作系统及浏览器版本。用户代理字符串有过一段相当长的发展历史,在此期间,浏览器提供商试图通过在用户代理字符串中添加一些欺骗性信息,欺骗网站相信自己的浏览器是另外一种浏览器。用户代理检测需要特殊的技巧,特别是要注意Opera会隐瞒其用户代理字符串的情况。即便如此,通过用户代理字符串仍然能够检测出浏览器所用的呈现引擎以及所在的平台,包括移动设备和游戏系统。

在决定使用哪种客户端检测方法时,一般应优先考虑使用能力检测。怪癖检测是确定应该如何处理代码的第二选择。而用户代理检测则是客户端检测的最后一种方案,因为这种方法对用户代理字符串具有很强的依赖性。

以下是完整的用户代理字符串检测脚本,包括检测呈现引擎、平台、Windows操作系统、移动设备和游戏系统。

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,
widMobile: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(engine.ver);
}else if(/Vsersion\/(\S+)/.test(ua)){
browser.ver = RegExp["$1"];
browser.safari = parseFloat(browser.ver);
}else{
//近似地确定版本号
var safariVersion = 1;
if(engine.webkit < 100){
safariVsersion = 1;
}else if(engine.webkei < 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(us) || /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
};
}();


IE的用户代理字符串:
IE7:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
IE8:Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0) (新增的Trident记号是为了让开发人员知道IE8是不是在兼容模式下运行,如果是,则MSIE的版本号会变成7,但Trident及版本号还是会留在用户代码字符串中)
IE9:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) (如果IE9运行在兼容模式下,字符串中的Mozilla版本号和MSIE版本号会恢复旧的值,但Trident的版本号仍然是5.0)

Firefox的用户代理字符串:
FireFox4:Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox 4.0.1

Safari的用户代理字符串:
Safari3.0: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的用户代理字符串:
Konqueror3.5:Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.0 (like Gecko) (Konqueror浏览器只能在Linux中使用)

Chrome的用户代理字符串:
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

Opera的用户代理:
Opera8:Opera/8.0 (Windows NT 5.1; U; en)
注:Opera在使用自己的用户代理字符串遇到问题时没有选择通过修改自身的用户代理字符串来迷惑嗅探代码,而是干脆选择通过修改自身的用户代理字符串件自身标识为一个完全不同的浏览器。Opera9以后,出现了两种修改用户代理字符串的方式。一种方式是将自身标识为另外一个浏览器,如Firefox或者IE。在这种方式下,用户代理字符串就如同Firefox或IE的用户代理字符串一样,只不过末尾加了字符串Opera及Opera的版本号。
Opera9.5:(1)Mozilla/5.0 (Windows NT 5.1; U; en; rv:2.0.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50 ----将Opera9.5标识为Firefox2,同时带有Opera版本信息。
(2)Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50 ----将Opera9.5标识为IE6,也包含了Opera版本信息

Opera标识自身的另一种方式,就是把自己装扮成Firefox或IE。在这种隐瞒真实身份的情况下,用户代理字符串实际上与其他浏览器返回的相同——即没有Opera字样,也不包含Opera版本信息。

Opera10.63:Opera/9.80 (Windows NT 6.1; U; en) Presto/2.6.30 Version/10.63

iOS和Android的用户代理字符串:
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

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

除了以上检测方法外,还有一种检测怪癖的方式:如,由于IE8及更早版本将NodeList实现为一个COM对象,而我们不能像使用JScript对象那样使用这种对象,因此用array=Array.propotype.slice.call(nodes,0);这样的代码会导致错误。要想在IE中将NodeList转换为数组,必须手动枚举所有成员。下列代码在所有浏览器中都可以运行:

function convertToArray(nodes){
var array = null;
try{
array=Array.propotype.slice.call(nodes,0);//针对非IE浏览器
}catch(ex){
array = new Array();
for(var i=0,len=nodes.length;i<len;i++){
array.push(nodes[i]);
}
}
return array;
}


这个convertToArray()函数首先尝试了创建数组的最简单方式。如果导致了错误(说明是在IE8及更早版本中),则通过try-catch块来捕获错误,然后手动创建数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值