每一种浏览器都有各自的长处,同时也存在各自的缺点。即使跨平台的浏览器,虽然从技术上看版本相同,也照样存在不一致问题。所以,或者迁就各方采用“最小公分母”策略;或者利用各种浏览器检测办法。
能力检测,又称为特性检测。能力检测的目的不是识别特定的浏览器,而是识别浏览器的能力,然后根据浏览器的能力给出解决方案。
理解能力检测,首先必须理解两个重要的概念。
例如:IE5.0之前的版本不支持document.getElementById()这个方法,使用非标准的document.all属性来实现。
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!');
}
}
检测某个属性是否存在并不能确定对象是否支持该方法,所以要尽量使用typeof进行能力检测;但宿主对象没有义务让typeof返回合理的值,在IE8及之前版本中,typeof document.createElement返回的是“object”,typeof new ActiveXObject('Microsoft.XMLHttp').open返回"unknown"。
下面这个方法考虑到了浏览器的怪异行为,不过也要注意,宿主对象没有义务保持目前的实现方式不变,也不一定会模仿已有宿主对象的行为,所以下列函数目前可靠,但不能保证永远可靠。
// 作者:Peter Michaux
function isHostMethod(object, property) {
var t = typeof object[property];
return t == 'function' ||
(!!(t == 'object' && object[property])) ||
t == 'unknown';
}
检测某个或某几个特性并不能确定浏览器。实际上,根据浏览器不同将能力组合起来是更可取的方式。如果你知道自己的应用程序需要使用某些特定的浏览器特性,最好是一次检测所有相关特性。
// 确定浏览器是否支持Netscape风格的插件
var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);
// 确定浏览器是否具有DOM1级规定的能力
var hasDOM1 = !!(document.getElementById && document.createElement && document.getElementsByTagName);
怪癖检测与能力检测不同
IE8及更早版本中存在一个bug,即如果某个实例属性与已标记为[[DontEnum]]的某个原型属性同名,那么该实例属性不会出现在for-in循环中。
var hasDontEnumQuirk = (function () {
var o = {toString: function () {}};
for (var prop in o) {
if (prop == 'toString') {
return false;
}
}
return true;
}());
Safari3以前版本中会枚举被隐藏的属性。
var hasEnumShadowsQuirk = (function () {
var o = {toString: function () {}};
for (var prop in o) {
if(prop == 'toString'){
return true;
}
}
return false;
}());
用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。在HTTP请求中,用户代理字符串作为响应首部发出,在客户端可以navigator.userAgent属性访问。
不到万不得已,就不要使用客户端检测。先设计最通用的方案,然后再使用特定浏览器的技术增强该方案。