JavaScript 客户端检测——“能力检测”的注意要点 - 前端学习笔记
客户端检测
不到万不得已,就不要使用客户端检测。只要能够找到更通用的方法,就应该优先采用更通用的方法。先设计最通用的方案,然后再使用特定于浏览器的技术增强该方案。
能力检测(性能检测)
基本模式语法
目标不是识别特定的浏览器,而是识别浏览器的能力。基本模式如下:
if (object.propertyInQuestion){ //使用object.propertyInQuestion}
举个例子,比如 IE5.0 之前的版本不支持 document.getElementById() 这个 DOM 方法。但可以使用 document.all[] 方法。于是可以写下如下代码:
function getElement(id){ if (document.getElementById){ return document.getElementById(id); }else if (document.getAll){ return document.getAll[id]; }else{ throw new Erroor("No way to retrieve element !"); }}
能力检测使用的要点
先检测达成目的的最常用的特性,可以保证代码最优化,并避免检测多个条件;
必须测试实际要是用到的特性;
对于第二点:
function getWindowWidth(){ if (document.all){ //假设是 IE 浏览器 return document.documentElement.clientWidth; //错误!不一定是 IE 浏览器 } else { return window.innerWidth; }}
如Opera 支持document.all,也支持window.innerWidth;所以上述代码用法上有问题。
更可靠的能力检测
能力检测对于想知道某个特性是否会按照适当方式行事非常有用。如检测对象是否支持排序:
function isSortable(obj){ return typeof obj.sort == "function";}var obj1 = [321,43215,1];var obj2 = { name: "Oliver", age: 18};console.log(isSortable(obj1)); //trueconsole.log(isSortable(obj2)); /false
这里需要注意的是,能力检测不是只检测相应的方法是否存在!!!
function isSortable(obj){ return !!obj.sort;}var obj1 = [321,43215,1];var obj2 = { name: "Oliver", age: 18, sort: true};console.log(isSortable(obj1)); //trueconsole.log(isSortable(obj2)); //true
这里就可以看出问题了,能力检测不是检测相应的方法是否存在,obj2 中定义了 sort 属性,仍然可以通过所谓的能力检测检测为 true。
所以在可能的情况下,要尽量使用 typeof
进行能力检测。
而在 IE 中,情况又不同了:
function hasCreateElement(){ return typeof document.createElement == "function";}
在 IE8 之前,这个函数返回 false,因为 typeof document.createElement 返回的是"object",而不是“function”。因为 IE 及更早版本中的宿主对象是通过 COM 而非 JScript 实现的。但 IE9 中纠正了这个问题,对所有 DOM 方法都返回“function”。
能力检测,不是浏览器检测
在实际开发中,应该将能力检测作为确定下一步解决方案的依据,而不是用他来判断用户使用的是什么浏览器。如:
var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);var hasDOM1 = !!(document.getElementById && document.getElementsByTagName && document.createElement);
上述代码一个是用来确定浏览器是否支持 Netscapte 风格的插件;另一个是用来确定浏览器是否具备 DOM1 级所规定的能力。
怪癖检测
目标是识别浏览器的特殊行为。怪癖检测是想要知道浏览器存在什么缺陷。如,IE8 及更早版本中存在一个 bug,即如果某个实例属性与[[Enumerable]]标记为 false 的某个原型属性同名,那么该实例就不会出现在 for-in 循环当中。可以使用以下代码来检测这种“怪癖”:
var hasDontEnumQuirk = function(){ var o = { toString : function(){} }; for (var prop in o){ if (prop == "toString"){return false; } } return true;}();
另外,在 Safari 3 以前的版本中会枚举被隐藏的属性。可以用下面的函数来检测:
var hasEnumShadowsQuirk = function(){ var o = { toString : function(){} } var count = 0; for (var prop in o){ if (prop == "toString"){count++; } } return (count > 1);}();
如果浏览器存在这个 bug,那么使用 for-in 循环枚举带有自定义的 toString() 方法的对象,就会返回两个 toString 的实例。