0%

JavaScript学习笔记(9)

《JavaScript高级程序设计》第九章,客户端检测。


浏览器之间差异很多,所以有时候我们不得不针对不同的浏览器做特定的操作,这就需要我们进行客户端检测了。

能力检测

能力检测的目标是识别浏览器的能力,而不是区分特定的浏览器。通常使用if语句来判断是否支持特定方法和功能,最好先判断通用的后判断特殊的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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!"); // 无方法则报错
}
}

// 确定浏览器是否支持Netscape风格的插件
var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);

// 确定浏览器是否有DOM1级规定的能力
var hasDOM = !!(document.getElementById && document.createElement &&
document.getElementByTagName);

需要注意的是,有时候我们不能想当然地认为某个特性会按照适当的方式行事,因此不能只是简单地判断方法存不存在。举个例子:

1
2
3
4
5
6
7
8
9
10
// 错误的能力检测:因为如果包含的sort是属性也会返回true
function isSortable(object) {
return !!object.sort;
}
var result = isSortable({sort: true}); // true

// 更准确的能力检测
function isSortable(object) {
return typeof object.sort == "funtion";
}

怪癖检测

怪癖检测(quirks detection)目标是识别浏览器的特殊行为,一般来说怪癖是缺陷。这种检测通常需要运行一小段代码,来确定某一性能不能正常工作。举个例子:

1
2
3
4
5
6
7
8
9
10
// 检测IE8前的一个bug:与[[DontEnum]]中属性同名的属性不会出现在for-in中
var hasDontEnumQuirk = function() {
var o = {toString: function(){}};
for(var prop in o) {
if(prop == "toString") {
return false;
}
}
return true;
}();

用户代理检测

用户代理检测是通过用户代理字符串来确定实际使用的浏览器,该字符串是在HTTP请求过程中作为响应首部发送的,可通过JavaScript的navigator.userAgent访问。它的发展历史很长,不同浏览器不同版本也有很多不一致的地方,也存在电子欺骗(spoofing)的问题,可以去翻书看看。这里拿我的浏览器信息举个例子:

1
2
console.log(nevigator.userAgent);
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"

识别呈现引擎

不管是什么浏览器,只要使用同版本的呈现引擎,就会具备相同的功能,因此我们可以在这个方面进行识别区分。主要有五大引擎:IE、Gecko、WebKit、KHTML、Opera。下面是一个检测实现的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
var client = function() {
var engine = {
ie: 0,
gecko: 0,
webkit: 0,
khtml: 0,
opera: 0,
ver: null
};
return {
engine: engine
};
}();

当检测到一个呈现引擎时,将client.engine对应的属性改为一个大于零的值。对于识别过程,我们需要制定检测的顺序,顺序不对则很可能会产生错误的结果。

  • 首先我们识别Opera,因为它的用户代理字符串很可能模仿别的浏览器。使用window.opera即可。
  • 其次识别WebKit,因为它的字符串中包含”Gecko”和”KHTML”,因此不能先识别它们俩。使用其字符串独有的”AppleWebKit”即可。
  • 然后识别KHTML,因为它的字符串中包含”Gecko”,因此不能先识别Gecko。使用其字符串中独有的”Konqueror”即可。
  • 然后识别Gecko。
  • 最后识别IE。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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引擎却不一样。因此我们需要在上面实现的基础上做一些添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var client = function() {
var engine = {
...
};
var browser = {
ie: 0,
firefox: 0,
safari: 0,
konq: 0,
opera: 0,
chrome: 0,
ver: null
};

return {
engine: engine,
browser: browser
};
}();

然后就是要确定检测顺序了,不过因为多数浏览器与其呈现引擎密切相关,所以我们会将对浏览器的检测嵌入到对呈现引擎的检测中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
// ...
} else if(/KHTML\/(S+)/.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
// ...
} else if(/MSIE ([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp["$1"];
engine.ie = browser.ie = parseFloat(engine.ver);
}

识别平台

在某些条件下,我们需要关注平台的差异性。目前的三大主流平台是:Windows、Mac、Unix。同样,我们可以在client对象中添加一个新对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var client = function() {
var engine = {
...
};
var browser = {
...
};
var system = {
win: false,
mac: false,
x11: false
}

return {
engine: engine,
browser: browser,
system: system
};
}();

不过在对平台的识别上,检测navigator.platform要比检测navigator.userAgent更简单,前者有以下几个取值:Win32、Win64、MacPPC、MacIntel、X11、Linux i686。这样我们就可以进行如下的判断:

1
2
3
4
var p = navigator.platform;
system.win = p.indexOf("Win") == 0;
system.mac = p.indexOf("Mac") == 0;
system.unix = (p.indexOf("X11") == 0) || (p.indexOf("Linux") == 0) ;

识别Windows操作系统

即识别Windows的各种版本,不赘述。

识别移动设备

即识别移动设备里的Web浏览器,可在system对象中添加iphone、ipod、ipad、ios、android、nokiaN、winMobile,不赘述。

识别游戏系统

即识别视频游戏系统里的Web浏览器,可在system对象中添加wii、ps,不赘述。