编写跨浏览器代码的核心原则
以下原则来自 IEBlog:- DO
- 特性检测(Feature Detection):使用之前检测浏览器是否支持这个特性。
- 行为检测(Behavior Detection):应用解决方法时先测试已知问题。
- DON'T
- 检测具体浏览器(Detect Specific Browsers):即浏览器检测。不要根据浏览器标识(如
navigator.userAgent
)应用页面行为。 - 假定无关特性(Assume Unrelated Features):检测到一个特性存在就假定其他特性也存在。
- 检测具体浏览器(Detect Specific Browsers):即浏览器检测。不要根据浏览器标识(如
基本应用
能力检测
能力检测(Feature Detection)就是识别浏览器的能力,根据不同的能力支持给出不同的解决方案。例如 IE 5.0 之前的版本不支持document.getElementById()
这个 DOM 方法,可以编写一个能力检测函数:
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!") } }能力检测需要注意两点:
- 先检测达成目标的最常用特性,避免测试多个条件。比如上面先检测标准的 DOM 方法。
- 检测实际用到的特性。不能检测到一个特性存在,就假定另外一个特性也存在(即假定无关特性原则)。
更可靠能力检测
在可能的情况下,应该尽量使用 typeof 进行能力检测。因为对象在具有某个属性而不是方法时也会返回 true 。需要注意的是,由于浏览器 bug 或者特性实现方式,typeof 也会返回不合理的值。
typeof document.createElement
IE8 及以前版本中上面的代码返回 object 而不是 function,因为这些版本的 DOM 功能通过 COM 对象实现。 IE9+ 修正了这个问题,所有 DOM 方法都返回 function。
IE 的 ActiveX 对象还有一个问题:
var xhr = new ActiveXObject("Microsoft.XMLHttp"); alert(typeof xhr.open); // 返回 "unknown",IE10 也是
因此,在浏览器环境下测试对象的某个特性可以使用 Peter Michaux 的方法:
function isHostMethod(object, property) { var t = typeof object[property]; return t=='function' || (!!(t=='object' && object[property])) || t=='unknown'; } // 使用示例 result = isHostMethond(xhr,"open") // true
还需要注意的是,能力检测不是浏览器检测,检测某个或某几个特性并不能够确定浏览器。如果需要使用某些特定特性,最好是一次性检测相关特性,而不要分别检测。
// 确定浏览是否支持 Netscape 风格插件 var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length); // 检查浏览器是否具备 DOM1 规定能力 var hasDOM1 = !!(document.getElementById && document.createElement && document.getElementsByTagName);
怪癖检测
怪癖检测(quirks detection),又称行为检测,即检测浏览器存在什么缺陷(怪癖也就是bug)。通常是运行一段代码,以确定某个特性不能正常工作。怪癖一般是个别浏览器独有的,对有程序有直接影响的怪癖,最好在脚本一开始就执行检测。如 IE8 及更早版本的一个 bug,即如果某个实例属性与标记为 [[DontEnum]] 的某个原型属性同名,那么该属性不会出现在 for-in 循环中。
var hasDontEnumQuirk = function(){ var o = { toString : function(){} }; for (var prop in o){ if (prop == "toString") { return false; } } return true; }();
正确的 ES 实现中,toString 应该在 for-in 循环中作为属性返回。
另外一个经常需要检测的“怪癖”是 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 实例。