用户代理检测指的是,检测用户代理字符串来确定用户实际使用的浏览器。在服务端,用户代理检测是一种常见且广为让人接受的方式;但是,在客户端,用户代理检测是一种万不得已才使用的方法,它经常被放在能力检测和怪癖检测之后。可以通过 navigator.userAgent 得到用户代理字符串。
用户代理经常跟电子欺骗联系在一起,因为浏览器会在自己的用户代理字符串中加入一些错误或者误导性的信息,来欺骗服务器。
用户代理字符串检测技术
一般情况下,直到呈现引擎和最低限度的版本就可以决定是否可以使用某些操作方法了。
if (isIE6 || isIE7) {
//call code 这种不推荐
}
上面的代码很脆弱,因为如果 IE 有了新版本,就必须更新代码。建议使用相对版本号作为条件判断:
if (isVer >= 6) {
//call code
}
- 识别呈现引擎
目前有这五种主要的呈现引擎:IE、Gecko、Webkit、KHTML、Opera。
为了不在全局作用域中添加多余的变量,我们使用模块增强模式来封装检测脚本,基础结构如下:
var client = function(){
var engine = {
//呈现引擎
ie: 0,
gecko:0,
weblit: 0,
khtml: 0,
opera: 0,
ver: null
};
return {
engine: engine
};
}
匿名函数内部定义了一个局部变量 engine,它是一个包含默认设置的对象字面量。如果检测到当前的浏览器的呈现引擎类型,就以浮点数值的格式将引擎的版本号写入相应的属性(大于 0)。引擎的完整版本号(字符串)写入 ver 属性。这样设计后,可以像这样写条件判断语句:
if (client.engine.ie){
//针对 IE
} else if (client.engine.gecko > 1.5){
if (client.engine.ver == "1.8.1"){
//针对这一版本执行某些操作
}
}
检测顺序很重要,因此第一步是识别 Opera,因为它可能完全模仿其他浏览器:
if (window.opera) {//opera 引擎
engine.ver = window.opera.version();//opera 7.6 及更高
engine.opera = parseFloat(engine.ver);
}
第二步检测 WebKit,它有一个独一无二的 “AppleWebKit”:
else if (/AppleWebKit\/(\S+)/.test(ua)) {//webkit 引擎
engine.ver = RegExp["$1"];//使用捕获组获得版本号
engine.webkit = parseFloat(engine.ver);
}
第三步检测 KHTML,,它也有一个独一无二的 “KHTML”,早期的版本有一个 “Konqueror”:
else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {//khtml 引擎
engine.ver = RegExp["$1"];
engine.khtml = parseFloat(engine.ver);
}
派出了 Webkit 和 KHTML 之后,就可以准确地检测出 Gecko,它的版本号出现在字符串“rv:”的后面:
else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {//gecko 引擎
engine.ver = RegExp["$1"];
engine.gecko = parseFloat(engine.ver);
}
最后是检测 IE:
else if (/MSIE ([^;]+)/.test(ua)) {//ie 引擎
engine.ver = RegExp["$1"];
engine.ie = parseFloat(engine.ver);
}
这里正则表达式中的取反符号,是为了取得不是分号的所有字符。
识别浏览器
为 client 对象添加了新属性:
var client = function () {
...
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) {//opera 引擎
engine.ver = browser.ver = window.opera.version();
engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)) {//webkit 引擎
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)) {//khtml 引擎
engine.ver = browser.ver = RegExp["$1"];
engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {//gecko 引擎
engine.ver = RegExp["$1"];
engine.gecko = parseFloat(engine.ver);
if (/Firefox\/(\S+)/.test(ua)) {//是 Firefox
browser.ver = RegExp["$1"];
browser.firefox = parseFloat(browser.ver);
}
} else if (/MSIE ([^;]+)/.test(ua)) {//ie 引擎
engine.ver = browser.ver = RegExp["$1"];
engine.ie = browser.ie = parseFloat(engine.ver);
}
可以这样使用上面设计的代码:
if (client.engine.webkit){//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 system = {
win: false,
mac: false,
x11: false,
};
...
return {
engine: engine,
browser:browser,
system: system
};
}
检测平台
//识别平台
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);
识别 Windows 操作系统
由于不同的浏览器在不同的 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"];
}
}
这样使用:
if (client.system.win){
if (client.system.win == "XP"){
//XP
}else if (client.system.win == "7"){
//win7
}
}
识别移动设备
为 system 对象添加新属性:
var client = function () {
...
//平台
var system = {
...
//移动设备
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;
检测 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);
}
检测诺基亚 N 系列手机:
system.nokiaN = ua.indexOf("NokiaN") > -1;
使用:
if (client.engine.webkit){
if (client.system.ios){
//iOS
}else if (client.system.android){
//Android
}
}
最后是 Windows Mobile(Windows CE):
if (system.win == "CE") {//Windows Mobile 5.0 以及之前版本
system.winMobile = system.win;
} else if (system.win == "Ph") {//Windows Mobile 5.0 以后版本
if (/Windows Phone OS (\d+.\d+)/.test(ua)) {
system.win = "Phone";
system.winMobile = parseFloat(RegExp["$1"]);
}
}
识别游戏系统
为 system 对象添加新属性:
var client = function () {
...
//平台
var system = {
...
//游戏系统
wii: false,
ps: false
};
...
return {
engine: engine,
browser:browser,
system: system
};
}
检测:
//识别游戏系统
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);
完整代码
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,
//游戏系统
wii: false,
ps: false
};
//检测呈现引擎、平台和设备
var ua = navigator.userAgent;//用户代理字符串
if (window.opera) {//opera 引擎
engine.ver = browser.ver = window.opera.version();
engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)) {//webkit 引擎
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)) {//khtml 引擎
engine.ver = browser.ver = RegExp["$1"];
engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {//gecko 引擎
engine.ver = RegExp["$1"];
engine.gecko = parseFloat(engine.ver);
if (/Firefox\/(\S+)/.test(ua)) {//是 Firefox
browser.ver = RegExp["$1"];
browser.firefox = parseFloat(browser.ver);
}
} else if (/MSIE ([^;]+)/.test(ua)) {//ie 引擎
engine.ver = browser.ver = RegExp["$1"];
engine.ie = browser.ie = parseFloat(engine.ver);
}
//识别平台
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);
//识别 Windows 操作系统
console.log("ua:" + ua);
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;
//检测 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);
}
//检测诺基亚 N 系列手机
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"]);
}
}
//识别游戏系统
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);
return {
engine: engine,
browser: browser,
system: system
};
}();
使用方法
应该优先使用能力检测和怪癖检测,用户代理检测方法尽量只用作最后检测的手段。它一般适用于以下情形:
- 不能直接准确地使用能力检测和怪癖检测。
- 同一种浏览器在不同平台下具有不同的能力。
- 为了跟踪分析等目的需要知道确切的浏览器类型。