web前端之JavaScript高级程序设计五:客户端检测
BOM:
BOM 提供了很多对象,用于访问浏览器的功能,这些功能与任何网页内容无关。多年来,缺少事实上的规范导致 BOM 既有意思又有问题,因为浏览器提供商会按照各自的想法随意去扩展它。于是,浏览器之间共有的对象就成为了事实上的标准。这些对象在浏览器中得以存在,很大程度上是由于它们提供了与浏览器的互操作性。 W3C 为了把浏览器中 JavaScript 最基本的部分标准化,已经将 BOM 的主要方面纳入了 HTML5 的规范中。
window 对象:
BOM 的核心对象是 window,它表示浏览器的一个实例。在浏览器中, window 对象有双重角色,它既是通过 JavaScript 访问浏览器窗口的一个接口,又是 ECMAScript 规定的 Global 对象。这意味着在网页中定义的任何一个对象、变量和函数,都以 window 作为其 Global 对象,因此有权访问parseInt()等方法。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>window 对象</title>
<script type="text/javascript">
var age=29;
function sayAge(){
alert(this.age);
}
alert(window.age);
sayAge();
window.sayAge();
</script>
</head>
<body>
</body>
</html>
由于 window 对象同时扮演着 ECMAScript 中 Global 对象的角色,因此所有在全局作用域中声明的变量、函数都会变成 window 对象的属性和方法。
能力检测:
最常用也最为人们广泛接受的客户端检测形式是能力检测(又称特性检测)。能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力。采用这种方式不必顾及特定的浏览器如何如何,只要确定浏览器支持特定的能力,就可以给出解决方案。
能力检测的基本模式如下:
if (object.propertyInQuestion){//属性问题
//使用 object.propertyInQuestion
}
检测浏览器支持获取id的哪种DOM方式:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>客户端检测</title>
<script type="text/javascript">
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!");
}
}
</script>
</head>
<body>
</body>
</html>
怪癖检测:
与能力检测类似, 怪癖检测(quirks detection)的目标是识别浏览器的特殊行为。但与能力检测确认浏览器支持什么能力不同,怪癖检测是想要知道浏览器存在什么缺陷(“怪癖”也就是 bug)。这通常需要运行一小段代码,以确定某一特性不能正常工作。
查询浏览器中枚举是否被隐藏:
var hasEnumShadowsQuirk=function(){
var o={
toString:function(){}
};
var count=0;
for(var prop in o){
if(prop == "toString"){
count++;
}
}
return (count > 0);
}();
用户代理检测:
用户代理检测通过检测用户代理字符串来确定实际使用的浏览器,通过浏览器相应的格式来进行判断。
识别呈现引擎:
不管是什么浏览器,只要它跟 Safari 3 使用的是同一个版本的 WebKit,那么该浏览器也就跟 Safari 3 具备同样的功能。因此,我们要编写的脚本将主要检测五大呈现引擎: IE、 Gecko、 WebKit、 KHTML 和 Opera。
var clien =function(){
var engine={
ie:0
,gecko:0
,webkit:0
,khtml:0
,opera:0
,ver:null
};
//在这检测呈现引擎,平台和设备
return {
engine:engine
};
}();
这里声明了一个名为 client 的全局变量,用于保存相关信息。匿名函数内部定义了一个局部变量engine,它是一个包含默认设置的对象字面量。在这个对象字面量中,每个呈现引擎都对应着一个属性,属性的值默认为 0。如果检测到了哪个呈现引擎,那么就以浮点数值形式将该引擎的版本号写入相应的属性。而呈现引擎的完整版本(是一个字符串),则被写入 ver 属性。作这样的区分可以支持像下面这样编写代码:
if(client.engine.ie){//如果是IE,cilent.ie的值应该大于0
//针对IE的代码
}else if(client.engine.gecko>1.5){
if(client.engine.ver=="1.8.1"){
// 正对这个版本执行某些操作
}
}
要正确地识别呈现引擎,关键是检测顺序要正确。由于用户代理字符串存在诸多不一致的地方,如果检测顺序不对,很可能会导致检测结果不正确。为此,第一步就是识别 Opera,因为它的用户代理字符串有可能完全模仿其他浏览器。我们不相信 Opera,是因为(任何情况下)其用户代理字符串(都)不会将自己标识为 Opera。
if (window.opera){
engine.ver = window.opera.version();
engine.opera = parseFloat(engine.ver);
}
应该放在第二位检测的呈现引擎是WebKit。因为WebKit 的用户代理字符串中包含”Gecko”和”KHTML”这两个子字符串,所以如果首先检测它们,很可能会得出错误的结论。不过,WebKit的用户代理字符串中的”AppleWebKit”是独一无二的,因此检测这个字符串最合适。下面就是检测该字符串的示例代码:
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。同样,KHTML的用户代理字符串中也包含”Gecko”,因此在排除KHTML之前,我们无法准确检测基于Gecko的浏览器。KHTML的版本号与WebKit的版本号在用户代理字符串中的格式差不多,因此可以使用类似的正则表达式。此外,由于Konqueror3.1 及更早版本中不包含KHTML的版本,故而就要使用Konqueror的版本来代替。下面就是相应的检测代码:
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);
}
最后一个要检测的呈现引擎就是 IE 了。 IE 的版本号位于字符串”MSIE”的后面、一个分号的前面,因此相应的正则表达式非常简单,如下所示:
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 引擎却不一样。在这两款浏览器中, client.webkit都会返回非 0值,但仅知道这一点恐怕还不够。对于它们,有必要像下面这样为 client 对象再添加一些新的属性:
var clien =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
};
}();
代码中又添加了私有变量 browser,用于保存每个主要浏览器的属性。与 engine 变量一样,除了当前使用的浏览器,其他属性的值将保持为 0;如果是当前使用的浏览器,则这个属性中保存的是浮点数值形式的版本号。同样, ver 属性中在必要时将会包含字符串形式的浏览器完整版本号。由于大多数浏览器与其呈现引擎密切相关,所以下面示例中检测浏览器的代码与检测呈现引擎的代码是混合在一起的。
//检测呈现引擎及浏览器
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 it’s WebKit
if (client.browser.chrome){
//执行针对 Chrome 的代码
} else if (client.browser.safari){
//执行针对 Safari 的代码
}
} else if (client.engine.gecko){
if (client.browser.firefox){
//执行针对 Firefox 的代码
} else {
//执行针对其他 Gecko 浏览器的代码
}
}
识别平台:
很多时候,只要知道呈现引擎就足以编写出适当的代码了。但在某些条件下,平台可能是必须关注的问题。那些具有各种平台版本的浏览器(如 Safari、 Firefox 和 Opera)在不同的平台下可能会有不同的问题。目前的三大主流平台是 Windows、 Mac 和 Unix(包括各种 Linux)。为了检测这些平台,还需要像下面这样再添加一个新对象
var clien =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
};
}();