Internet Explorer 10 兼容性白皮书(二) 用HTML5构建应用程序
用HTML5构建应用程序
HTML5的采用策略:一个浏览器也不放弃 点击链接下载示例代码 HTML5有许多激动人心的特性。有了新的标签、新的CSS能力和新的JavaScript API,Web的能力范围有了大的飞跃。除了浏览器厂商的士气高涨之外,令人激动的新功能列表几乎每天都在增加。从“nightly builds”(每夜都构建一版)到开发渠道发行版和正常的平台预览版,浏览器在飞速变化,世界各地Web开发人员们正在加入这个狂欢。
但是,尽管开发和浏览器社区正在把HTML5的喧嚣推到一个极度兴奋的高潮,网上的大多数人却不像我们一样使用最新的浏览器和最新版本。如果你是一个大型开发机构的Web开发人员或者是拥有庞大用户群的大企业,那你对此可能很清楚。即使你为通过Web提供服务的小型机构或新创立的企业工作,你可能也要花上大量时间来确保自己的网站能够支持尽可能多的浏览器和浏览器版本。
基于这一现实,很容易看出,HTML5还谈不到它是否已为当今的使用做好准备,而是你是否为它做好了准备。例如,假设你用一些新的语法标签(例如 和 )新建了一个页面,添加了一些新的CSS功能(例如圆角(border-radius)和阴影(box-shadow)),甚至添加了一个 在较新的浏览器上,例如IE9、Firefox 4及以上版本、或者Google Chrome上。但如果尝试在IE8或更早的浏览器上加载页面,很有可能看到的是图2所示的效果:一个残缺不全的页面。 如果你在研究HTML5的所有强大功能却得到上述体验之后告诉自己说:最好还是等等,那么我不会对你有任何责怪。如果我问你准备好了吗?你很容易得出这样的结论:HTML5还没有为你或你的用户做好准备。
在你决定等到2022年再考虑HTML5的之前,我建议你继续阅读本文的后面部分,我将向你提供一些实用的策略,让你现在就能采用HTML5技术,同时避免出现图2所示的糟糕的降级情况。我将从下面三个主题进行详细地介绍:
- 功能检测与用户代理(UA)嗅探比较
- 用JavaScript实现填补(Polyfill)
- 优雅降级
这些应该可以教会你很多构建支持各种浏览器的网站所需要了解的知识。在本文结束时,你会拥有一个可靠的策略,可以充满自信、毫不犹豫地采用HTML5技术。你还会拥有一些工具在手,可以逐步地为新浏览器增强网站,同时更好地适应旧的浏览器。
功能检测的重要性
为了提供跨浏览器的稳定且一致的体验,开发人员经常需要获得一些关于用户浏览器的信息。以前的普遍做法是像下面这样用JavaScript检测这些信息:
varuserAgent = navigator.userAgent;
if (userAgent.indexOf('MSIE') >= 0) { console.log("Hello, IE user"); } else if (userAgent.indexOf('Firefox') >= 0) { console.log("Hello, Firefox user"); } else if (userAgent.indexOf('Chrome') >= 0) { console.log("Hello, Chrome user"); } 这个技术称为用户代理(UA)嗅探,广泛地用于判断正在请求页面的是哪个浏览器。这里的思路是:知道了用户的浏览器(例如IE7),就能在运行的时候决定启用或禁用网站的哪项功能。UA嗅探就相当于对浏览器说:“你是谁?”(对UA嗅探以及其他检测技术的深入分析,请参阅bit.ly/mlgHHY。) 这种做法的问题在于,浏览器会撒谎。UA字符串是一个用户可以配置的信息,并不会提供100%正确的浏览器信息。而且,随着这一技术的广泛采用,许多浏览器厂商在自己的UA字符串中增加了额外内容,用来欺骗脚本,让脚本对于实际使用的浏览器做出错误判断,从而避免检测。现在有些浏览器甚至提供小工具,允许用户只要轻轻点击几下鼠标,就能修改UA字符串。 UA嗅探的目的从来就不是确定用户的浏览器和版本。而且它肯定也不是为了在你不喜欢用户使用的浏览器时,让你可以告诉用户说“请下载另一个浏览器”——即使有些人就是这样使用UA嗅探技术的。用户有权选择自己使用什么浏览器,开发人员的职责则是提供最可靠且一致的体验,不要把浏览器的偏好强加给用户。UA嗅探的目标是让你能够准确地了解在用户当前的浏览器中,有哪些能力或功能可以利用。对浏览器本身的了解,只是获得这些信息的一个途径。 目前有一些UA嗅探的替代技术,其中一项正在日益流行的技术称为对象检测或功能检测。这两个术语多数时候可以互换使用,但本文统一使用“功能检测”(feature detection)。 功能检测的目标是判断某项功能或能力在用户当前的浏览器中是否受支持。如果UA嗅探是问浏览器“你是谁”,“功能检测”就是问浏览器“你能干什么”,这个问题更直接,对于根据条件向用户提供功能来说,这种方法也更可靠。如果功能检测脚本实现正确,用户或浏览器将很难造假或错报功能支持。 手动功能检测
那么,与UA嗅探的示例相比,功能检测到底是什么样呢?为了回答这个问题,我们先来看看如果在IE8中查看前面的HTML5页面,如何解决出现的问题。
My Awesome SiteMy Awesome SiteAnArticleIsn't this awesome?
IE9和IE8的显示效果有很大的差别。对于初学者来说,我的页面完全没有样式,因为这个页面的CSS并不存在。而且,页面底部丢失了好玩的HTML5盾牌。每个问题都可以轻松解决,而功能检测则是明确问题的第一步。 两个问题的原因都很简单:对于IE8来说, 、 和
!!document.createElement('canvas').getContext
这条语句做了好几件事。首先,它使用两个否定(!!)操作符强行将未定义的值显式地设为false。然后,它手动新建一个canvas元素,并将它加到DOM中。最后,它调用getContext函数,这是 这是最基本的功能检测。利用这条语句以及其他类似语句,就有了查询浏览器所支持功能的更可靠方法。关于手动功能检测的更多信息,请参阅diveintohtml5.info/everything.html。 使用Modernizr进行功能检测
手动功能检测肯定是对UA嗅探的提高,但这种做法仍然需要你做大量工作来检测功能是否可用,以及在功能不存在的时候决定做什么。虽然Canvas示例很简单,只需要一行代码,但不是每个要检测的功能都这么简单——不同浏览器的检测代码也各不相同。例如,要检测是否支持前面使用的CSS3模块(border-radius和box-shadow)就有些麻烦。 值得庆幸的是,Modernizr (modernizr.com) 提供了更好的方法。Modernizr是一个JavaScript库“……检测下一代 Web 技术(即源于HTML 5和CSS3规范的功能)的本地实现是否可用”。在页面上添加对Modernizr的引用可以提供四大功能:
- 全面列出支持的功能,智能地加入标签,从而实现CSS的条件定义。
- 一个JavaScript对象,方便进行基于脚本的功能检测。
- 在运行的时候将全部HTML5新标签加入DOM,方便IE8和之前的IE浏览器(稍后就会知道不仅如此)。
一个脚本加载器,可以根据条件将polyfill加载到页面中。 本文对第1项不做进一步介绍,但鼓励你访问modernizr.com网站,学习这一功能及其余功能的文档。 上面的第2项功能,可以将下面的代码:
!!document.createElement('canvas').getContext 改为这行代码:
Modernizr.canvas 这行代码会返回一个布尔值,表明页面是否支持Canvas元素。使用Modernizr比自行执行功能检测的好处是,Modernizr是一个经过良好测试、健壮的、广为采用的库,它已经完成了许多繁重的工作。Twitter、Google、Microsoft以及无数其他机构和开发人员都在使用Modernizr,你当然也可以使用。在ASP.NET MVC 3工具更新(2011年4月发布)中,Microsoft甚至随新的ASP.NET MVC应用程序一起配备了Modernizr。当然,我们迄今为止所做的,不过是检测是否支持
if (Modernizr.canvas) { // 这里执行canvas代码。 } 根据附加的浏览器功能是否存在来给网站增加功能,这种做法称为“渐进式增强”,因为体验增强针对的能力更强的浏览器。另一方面是“优雅降级”,即某项功能的缺失不会造成浏览器出错或发生故障,而是应该向用户提供一些削弱的功能或替代能力。对于旧版浏览器来说,不必将优雅降级作为默认选择。在许多情况下,甚至可能不是最佳选择。相反,在Modernizr的帮助下,你通常可以使用许多可用的浏览器polyfill,将类似于HTML5的功能添加到不支持HTML5的浏览器中。 什么是Polyfill?
根据Modernizr网站的说法,polyfill是“在旧版浏览器上复制标准API的JavaScript补充”。“标准API”指的是HTML5技术或功能,例如Canvas。“JavaScript补充”指的是可以动态地加载JavaScript代码或库,在不支持这些标准API的浏览器中模拟它们。例如,geolocation(地理位置)polyfill可以在navigator对象上添加全局的geolocation对象,还能添加getCurrentPosition函数以及“坐标”回调对象,所有这些都是W3C地理位置API定义的对象和函数。因为polyfill模拟标准API,所以能够以一种面向所有浏览器未来的方式针对这些API进行开发,最终目标是:一旦对这些API的支持变成绝对大多数,则可以方便地去掉polyfill,无需做任何额外工作。 通过在页面上添加对Modernizr的引用,我就得到了与示例相关的polyfill的直接好处。页面显示没有样式,是因为IE8不认识 和 标签。因为它不认识这些标签,所以没将它们加入DOM,而CSS选择元素要发挥样式作用,需要在DOM中有这些元素。 当我在页面上添加 。我之所以得到这个好处,是因为Modernizr用JavaScript(document.CreateElement(‘nav’))手动地将所有HTML5的新标签添加到DOM,这样CSS就能选择标签并给标签加上样式。 除了在IE中添加对新的HTML5元素的支持外,Modernizr库默认不提供任何额外的polyfill。额外的polyfill需要自行提供,或者使用自己的脚本,或者从Modernizr网站上日益增加的选项列表中选择一个。在2.0版中,Modernizr提供了一个条件脚本加载器(基于yepnope.js—yepnopejs.com),可以帮助你只在需要的时候异步下载polyfill库。使用Modernizr配合一个或多个polyfill库来提供需要的功能,是一个强大的组合 使用Polyfill模拟HTML5功能
对于Canvas来说,在Modernizr和JavaScript库excanvas的帮助下,用polyfill可以在IE8及之前的版本中实现Canvas支持,可以在IE6、IE7和IE8上添加API级别的Canvas支持。可以从bit.ly/bSgyNR下载excanvas,将它添加到自己的脚本文件夹,然后在页面的脚本块中添加一些代码。 用Modernizr和Polyfill实现Canvas支持
Modernizr.load({
test: Modernizr.canvas, nope: '../js/excanvas.js', complete: function () { Modernizr.load('../js/html5CanvasLogo.js'); } }]);
在这里,我使用Modernizr脚本加载器指定了三件事:
- 用来测试的Boolean表达式。
- 表达式测试为false时,加载脚本的路径。
检查或脚本加载完成时运行的回调。 对于Canvas来说,在应用程序中要添加的智能处理和polyfill就是这些了。Modernizr会异步加载excanvas.js,而且只会为那些不支持Canvas的浏览器加载,然后加载脚本库,在页面上绘制出HTML5标识。 下面来看另外一个示例来理解Modernizr的价值. 重视细节的你可能已经注意到,图4中网站的样式与图1中在IE9中显示的原始页面相同。这个页面在IE8中显示时没有阴影和圆角,而我不可能让网站在没有这两个效果的情况下丑陋地交付出去,所以我们再次求助于Modernizr。 同处理Canvas一样,Modernizr可以告诉我CSS模块不受支持,但提供一个填补(polyfill)这些CSS模块的库,要我来做。幸运的是,有个名为PIE (css3pie.com)的库在一个库中提供了这两个模块。 为了增加对圆角和阴影的支持,在下载了PIE之后,我可以将图6中的代码加入脚本。这次,我要测试是否支持圆角或阴影模块(而不是假定两者都支持或都不支持),如果有哪个模块不支持,则动态地加载PIE.js。PIE加载完成后,我再执行一段jQuery代码选择全部 标签,并调用PIE.attach函数,添加CSS中已经定义的圆角和阴影样式。
Modernizr.load({ test: Modernizr.borderradius || Modernizr.boxshadow, nope: '../js/PIE.js', callback: function () { $('article').each(function () { PIE.attach(this); }); } });
使用polyfill协助进行优雅降级 除了使用这里讨论的polyfill技术,在希望应用程序优雅降级的地方也可以借助于Modernizr,而不是用另一个库进行填补(polyfill)。 假设网页上有一个Bing Maps控件,而且我希望使用Geolocation(我将在以后的文章中深入介绍)来确定用户当前位置,然后将这个位置作为大头钉放在地图控件上。 虽然新版本的浏览器都支持Geolocation,但在旧版浏览器中并不支持。纯粹使用JavaScript提供完整的Geolocation支持还确实有些麻烦,即使有针对Geolocation的填补(polyfill)实现起来也不轻松,所以我决定要对自己的应用程序进行优雅降级。当用户的浏览器不支持Geolocation时,我会提供一个表单,用户可以在表单中手动输入位置,我将用用户输入的位置定位和固定地图。 通过Modernizr,只要用一个简单的加载脚本,调用我创建的两个脚本中的一个即可,如图8所示。在这个示例中,我测试的是Modernizr.geolocation属性。如果为true(“yep”分支),就加载fullGeolocation.js脚本,这个脚本使用Geolocation API定位(要得到用户许可),并将位置放在地图上,如图9所示。如果测试为false(“nope”分支),则加载备用脚本,在页面上显示一个地址表单。用户提交表单时,我会使用用户提供的地址将地图居中并固定,如图10所示。这样,我的页面为最新的浏览器提供了优秀的体验,同时对旧版浏览器提供了降级到合理替代品的优雅方式。
Modernizr.load({
test: Modernizr.geolocation, yep: '../js/fullGeolocation.js', nope: '../js/geolocationFallback.js' });
在面对庞大的用户群仍然使用旧版浏览器的时候,很容易认为HTML5的一些高级功能对你的网站不适用。但是现在已经有了很好的解决方案,不仅能够帮助你优雅地降级,还能提升旧版浏览器的能力,让你的用户立即就能体验到HTML5的能力。在本文中,你看到了功能检测、Modernizr和polyfill,所以你可以毫不迟疑地采用HTML5,既满足日益增长的使用最新浏览器的用户,又不会丢失使用旧版浏览器的庞大用户群。 以上内容来自于:http://msdn.microsoft.com/en-us/magazine/hh394148.aspx