兼容性和互用性
Web 浏览器是 Web 应用的操作系统,但是 Web 是一个存在各种差异的环境,Web 文档和应用会在不同操作系统不同时代的浏览器中查看和运行,因此,写一个健壮的客户端 JavaScript 程序并在这么多不同类型的平台上能够正常运行,的确是一个挑战。
客户端 JavaScript 兼容性和交互性的问题可以归为以下三类:
- 演化:Web 平台一直在演变和发展中,新标准新特性推出后,需要浏览器实现,再由开发者使用,才能推广,有时候反过来浏览器和开发者反过来引领这种标准规范的制订,此外,有些情况下,新老版本浏览器对新特性的支持度也不尽相同,需要开发者去权衡兼容性
- 未实现:不同浏览器对某个特性的支持也不一样,有些实现了,有些没有实现
- bug:每个浏览器都有bug,并且没有按照规范准确地实现所有客户端 JavaScript API
尽管有这么多兼容性问题,好在 JavaScript 语言本身是被所有浏览器实现的,并且浏览器对 ECMAScript 的实现都是相互通用的。
解决 JavaScript 的兼容性问题需要先了解问题的根源是什么,不同 Web 浏览器版本的更迭非常频繁,要想实时了解什么版本的浏览器实现了哪些特性,可以去下面这些站点查找:
- Mozilla 开发者中心
- Chrome 开发者中心
- Microsoft 开发者网络
- Apple Safari 开发者中心
- 维基百科跟踪 HTML 5 特性和 API 在各个浏览器的实现状态
- 维基百科跟踪 DOM 特性的实现状态
- 跟踪重要 Web 特性的实现状态
- 根据标准 W3C 标准列出的各种浏览器的 DOM 兼容性表格
- 另一个跟踪浏览器开发商对 Web 标准实现的站点
当然,意识到浏览器之间的兼容性只是第一步,接下来,需要解决这些兼容性,一种策略是只使用你选择支持的浏览器都普遍支持的特性,这个比较好理解,下面我们来介绍一种略有点消极的对待客户端不兼容性问题的策略。
处理兼容性问题的类库
处理不兼容问题其中一种最简单的方法就是使用类库。比如 IE 8 及以下版本现在不支持 <canvas>
,但它支持一种晦涩的客户端图形语言,叫做 VML,canvas
元素可以基于它进行模拟,开源的 ExplorerCanvas 项目就是为此开发的一个类库。
但是,有时候不可能完全在一个不支持某个特性的浏览器上实现一个特性,就像 IE 是唯一没有实现标准事件处理 API 的浏览器,包括注册事件处理函数的 addEventListener()
方法,IE 支持一个类似的方法叫 attachEvent()
,通常在这种情况下开发者需要定义一个折中的事件处理方法,比如 addEvent()
,在该方法中根据浏览器类型调用 addEventListener()
或 attachEvent()
来实现事件绑定功能,然后在所有代码里用 addEvent()
替代 addEventListener()
或 attachEvent()
。
在实际开发工作中,不少 Web 开发者都会在所有 Web 页面使用客户端 JavaScript 框架,如 jQuery,这些框架会定义新的客户端 API 并兼容所有浏览器,从而免去对不同浏览器兼容性的考量,大大提升开发效率。
分级浏览器支持
分级浏览器是由 Yahoo! 率先提出的一种测试技术,从某种维度对浏览器厂商/版本/操作系统变体进行分级。分级浏览器中的A级要通过所有的功能测试用例,对于C级浏览器来说则不必所有用例都通过测试,A级浏览器需要网页完全可用,C级浏览器只需在HTML完整的情况下可用即可,而不需要JavaScript和CSS都正常工作。那些不是A级和C级的浏览器都称作X级浏览器:这部分是全新的或者罕见的浏览器,我们默认在这些浏览器中都是网页完全可用的,但官方并不会对X级浏览器中的功能提供完整测试和支持。
更多关于分级浏览器的细节,请参考 https://github.com/yui/yui3/wiki/Graded-Browser-Support。
功能测试
功能测试是解决不兼容性问题的强大技术,如果你想试用某个功能,但又不清楚这个功能是否在所有浏览器中都有比较好的兼容性,则需要在脚本中添加相应的代码来检测是否在浏览器中支持该功能:
if (window.addEventListener) {
element.addEventListener('keydown', handler, false);
} else if (window.attachEvent) {
element.attachEvent('onkeydown', handler);
} else {
element.onkeydown = handler;
}
关于功能测试最重要的是它并不涉及浏览器开发商和浏览器的版本号,代码在当前浏览器集合中有效,在浏览器后续的版本中也同样有效,而不管后续浏览器是否实现了这些功能集合。
怪异模式和标准模式
Microsoft 在发布 IE6 的时候,增加了 IE5 里没有的很多 CSS 标准特性。但为了确保与已有 Web 内容的后向兼容性,它定义了两种不同的渲染模式。在「标准模式」或「CSS兼容模式」中,浏览器要遵循 CSS 标准,在「怪异模式」中,浏览器表现的和 IE4 和 IE5 中的怪异非标准模式一样。渲染模式的选择依赖于HTML 文件顶部的 DOCTYPE
声明,在 IE6 中打开没有 DOCTYPE
的页面或声明了某些权限 DOCTYPE
的页面都会按照怪异模式进行渲染,定义了严格的 DOCTYPE
的页面(或者为了做到前向兼容性而添加了未知的 DOCTYPE
的页面)会按照标准模式进行渲染,定义了 HTML5 DOCTYPE
(<!DOCTYPE htm1>
)的页面在所有现代浏览器中都会按照标准模式渲染。
怪异模式和标准模式之间的差别经历了很长时间的发展历程,现在新版本的 IE 都支持标准模式,其他主流浏览器也都支持标准模式。这两种模式都已经被 HTML5 规范所认可。怪异模式和标准模式之间的差异对于 HTML 和 CSS 开发者影响最大。但客户端 JavaScript 代码则是需要知道文档以哪种模式进行渲染的。要进行这种渲染模式的特性检测,通常检查 document.compatMode
属性。如果其值为「CSS1Compat」,则说明浏览器工作在标准模式;如果值为「Back Compat」(或 undefined
,说明属性根本不存在),则说明浏览器工作在怪异模式。所有现代浏览器都实现了 compatMode
属性,并且 HTML5 规范对它进行了标准化。
浏览器测试
功能测试非常适用于检测大型功能领域的支持,比如可以使用这种方法来确定浏览器支持 W3C 事件处理模型还是 IE 的事件处理模型。另外,有时候可能会需要在某种浏览器中解决个别的 bug 或难题,但却没有太好的方法来检测 bug 是否存在。在这种情况下,需要创建一个针对某个平台的解决方案,这个解决方案和特定的浏览器厂商、版本或操作系统(或三者结合)联系紧密。
在客户端 Javascript 中检测浏览器类型和版本的方法就是使用 Navigator 对象,我们将在下一章学习它,确定当前浏览器的厂商和版本的代码通常叫做浏览器嗅探器或者客户端嗅探器。在 Web 早期,当 Netscape 和 IE 平台两者相互不兼容的时候,客户端嗅探是一种常见的客户端编程技术,现在兼容性情况已经基本稳定,浏览器嗅探不像若干年前这样常用,但偶尔有些场景还会用到。
需要注意的是,客户端嗅探也可以在服务器端完成,Web 服务器根据 User-Agent
头部可以有选择地返回特定的 JavaScript 代码给客户端。
Internet Explorer 里的条件注释
实际上,你会发现客户端 JavaScript 编程中的很多不兼容性是针对 IE 的,也就是说,必须按照某种方式为 IE 编写代码,而按照另一种方式为其他浏览器编写代码。IE 支持条件注释,尽快这种做法不符合标准规范,但是在处理不兼容性时非常有用。下面是 HTML 条件注释的示例:
<!--[if IE 6]-->
这里的内容只会在IE6中显示
<![endif]-->
<!--[if lte IE 7]-->
这里的内容只会在<=IE7的浏览器中显示,还支持 lt、gt、gte
<![endif]-->
<!--[if !IE]> <-->
这里的内容只会在非IE浏览器中显示
<!--> <![endif]-->
这里的内容会在所有浏览器中显示
IE 的 JavaScript 解释器也支持条件注释,以 /*@cc_on
开头,以 @*/
结束,通过注释条件和常规的 JavaScript 注释的合理的交叉组合,可以设置在 IE 中运行一段代码而在其他浏览器中运行另一段代码:
/*@cc_on
@if (@_jscript)
// 这里的代码在条件注释中,也在常规的JavaScript注释中
// IE 会执行这段代码
alert("你使用的是IE浏览器");
@else*/
// 这段代码并没在 JavaScript 注释中,但仍在 IE 条件注释中
// 非 IE 浏览器都会执行这段代码
alert("你使用的不是 IE 浏览器");
/*@end
@*/
注:由于 JScript 是 Microsoft 自己的 JavaScript 解释器的名字,所以
@_jscript
在 IE 中总是返回true
。
No Comments