传统浏览器目前不会被完全取代,令你难以将最新的 CSS3 或 HTML5 功能嵌入你的网站。 Modernizr 正是为解决这一难题应运而生,作为一个开源的 JavaScript 库,Modernizr 检测浏览器对 CSS3 或 HTML5 功能支持情况。 Modernizr 并非试图添加老版本浏览器不支持的功能,而是令你通过创建可选风格配置修改页面设计。 它也可以通过加载定制的脚本来模拟老版本浏览器不支持的功能。
Modernizr 简单易用,但不是万能的。 成功使用 Modernizr 很大程度上取决于你的 CSS 和 JavaScript 技能。 通过本指南,你可以了解如何为不支持多栏或投影的浏览器设置可选风格。 此外,你还可以了解如何让老版本浏览器对使用最新 HTML5 要求(required)属性的表单进行验证,以及如何根据浏览器的功能有选择地加载脚本文件。
Modernizr 是一个用来检测浏览器功能支持情况的 JavaScript 库。 目前,通过检验浏览器对一系列测试的处理情况,Modernizr 可以检测18项 CSS3 功能以及40多项关于HTML5 的功能。 它比传统检测浏览器名称(浏览器嗅探)的方式更为可靠。 一整套测试的执行时间仅需几微秒。 此外,Modernizr 网站通过定制脚本只对你感兴趣的元素进行检测,从而实现效率优化。
当使用 Modernizr 检测 CSS3 支持情况时,你无需具备任何 JavaScript 的知识。 你仅需在网页中插入文件,它随即根据浏览器的功能情况在页面的<html>标签上添加一组类。 相应类的名称已经符合标准化的要求并浅显易懂。 例如,如果浏览器支持 box-shadow 属性,则需要添加相应的 boxshadow 类;否则,添加一个 no-boxshadow类即可。 你所要做的一切只不过是创建一个使用这些类的式样表,以便为相应浏览器提供合适的式样。
Modernizr 可轻松实现 JavaScript 解决方案,即人们熟知的 polyfills-它模拟HTML5 相关功能和技术,如地理定位。 然而,你的确需要对 JavaScript 有基本了解以便使用这些功能和技术。 术语polyfill来源于一种填补裂缝的黏土的英国品牌Polyfilla(即美国人熟知的填泥料)。 这里,polyfill 用来填补浏览器功能上的漏洞。 有时,Modernizr 可无缝地执行这项任务。 但本质上,这只是一种修补工作,所以,不能依赖它产生无漏洞浏览器所实现的完全相同结果。
与其它 JavaScript 库相同的是,Modernizr 可提供 development 和 production 版本。 与大多数库相比,唯一的不同之处是,空格和注释已经从 production 版本中删除了,这样可以减少下载量的大小。 Modernizr 采取了不同的方法。 development 版本在某种意义上可称为是厨房中的水槽—它几乎包含了一切。 production 版本只包含了你选择的那些元素,这样能够显著降低下载量。 在很多情况下,production 版本可以缩小为development版本大小的二十分之一。
当使用 Modernizr 进行测试时,我建议你下载 development 版本。 一旦你掌握了它是如何运作的以及它的能力,你就可以下载一个自定义 production 版本以便在你的网站进行部署。
本教程的示例文件包含的是 Modernizr的development 2.0.6 版本,但是我建议你从 Modernizr 站点上用最新更新的版本替代它。
注:如果你错误地单击了主导航菜单中的 Download 链接,那么你将看到大量的复选框出现,要求你去选择你想要的工具。 这是为定制的 production 版本所设置。 单击面板顶部的 Development 版本的链接,或者单击浏览器的 Back 按钮返回主页并选中图1所示的 Development 按钮。
如前所述,Modernizr 并没有尝试在旧版本的浏览器中添加新的功能,但是它允许你按照你的风格去弥补那些缺失的功能。 为了展示这是如何运作的,示例文件中包含了一个叫做 css_support_begin.html 的页面。如果你在新式浏览器中加载这个页面,它应该看起来如图2所示。
图2. Firefox 5以多栏的格式显示了页面并且在图像中添加阴影该页面的风格是使用了CSS3的column-count和box-shadow属性,以便以多栏的格式显示文本并在图像中添加阴影。 旧版本的浏览器不支持这些属性中任何一项,因此在 Internet Explorer (IE) 7 中同一页面看起来如图3所示。
在IE9中,同一页面显示了图像阴影,但是文本和图3中所示的布局是一样的。
你在尝试弥补缺失的功能上采取什么方式取决于你的设计大纲的要求。 要尝试使页面在所有的浏览器中看起来完全一样将要涉及大量工作,但是你可以做一些简单的改进,比如在图像周围环绕文字,调整图像与文本的左边距,以及在图像底部和右边缘添加隐约的边界,以便使它更具三维立体的感觉。
Modernizr 使用 JavaScript 检测浏览器所支持的功能,但是,它并不是使用 JavaScript 动态地加载不同的样式表,而是使用非常简单的技术将类添加到页面的<html>标签。然后作为设计者由你决定使用 CSS 层叠为目标元素提供合适的样式。例如,如果页面支持box-shadow属性,那么 Modernizr 会添加boxshadow类。如果不支持,那么它用no-boxshadow类作为替代进行添加。
由于浏览器忽略它们无法识别的 CSS 属性,因此你可以放心地按照你的基本样式规则使用box-shadow属性,然而需要按照下面的格式为旧版本的浏览器添加单独的descendant selector:
.no-boxshadow img { /* styles for browsers that don't support box-shadow */ } 只有不支持box-shadow的浏览器才会有no-boxshadow类,因此其它的浏览器不会应用这个样式规则。
让我们将 Modernizr 添加到示例页面并检查它添加到<html> 标签中的类。
<!DOCTYPE HTML> <html> <!DOCTYPE HTML> <html class=no-js> Modernizr 依赖于在浏览器中启用的 JavaScrip。 当它启用时,这种类会被动态地删除。 但是,在极少数情况下,当 JavaScrip 没有启用时,它依然存在于 HTML makup 中,如果必要,它允许你为这样的访问者创建特殊的样式规则。
</style> <script src=js/modernizr.js></script> </head> 注:如果你使用 Insert 面板或者Insert菜单,则 Dreamweaver 会添加type=text/javascript到开始的 <script> 标签中。 这在 HTML5 中不再要求,但是留着它并不会造成危害。
注:如果你的 Dreamweaver 版本没有 Live Code(或者你正使用不同的 HTML 编辑器),那么你可以使用大多数新式浏览器提供的开发工具或者 Firefox 浏览器提供的 Firebug 检查生成的代码。
如图4所示,no-js类已经被js类替代,这表明 JavaScript 已经启用。
表1列举了 Modernizr 使用的类名称以表明对 CSS3 的支持。 如果某个功能不支持,那么相应类的名称用no-作前缀。
表1.Modernizr 检测的 CSS3 功能
CSS 功能 | Modernizr 类(属性) |
@font-face | fontface |
::beforeand::afterpseudo-elements | generatedcontent |
background-size | backgroundsize |
border-image | borderimage |
border-radius | borderradius |
box-shadow | boxshadow |
CSS animations | cssanimations |
CSS 2D transformations | csstransforms |
CSS 3D transformations | csstransforms3d |
CSS transitions | csstransitions |
flexible box layout | flexbox |
gradients | cssgradients |
hsla() | hsla |
multi-column layout | csscolumns |
multiple backgrounds | multiplebgs |
opacity | opacity |
reflection | cssreflections |
rgba() | rgba |
text-shadow | textshadow |
无论在哪对特定的CSS属性进行测试,类的名称和属性名称都是一样的,然而这要求去除任何连字号或是括号。 其它类是按照它们参考的CSS3模块而命名。
参见表1,你可以看到Modernizr使用boxshadow和csscolumns分别表明了对box-shadow属性和多栏布局的支持。 因此,你可以使用no-boxshadow和no-csscolumns类为不支持这些功能的浏览器创建特殊的样式规则。
为了保证指令简单,我将演示只有CSS 声明的范例。 你可以直接地将它们键入到Code视图中或者使用New CSS Rule对话框。
.no-boxshadow img创建一个新的descendant (compound) selector。#8A8A8A)。 由此产生的样式规则应该如下所示:.no-boxshadow img { border-right: #8A8A8A 2px solid; border-bottom: #8A8A8A 2px solid; } 这不像半透明的阴影那样有吸引力,但是尽管如此,它仍然使得图像能够从背景中略微地突起。
.no-csscolumns img创建一个新的descendant selector。.no-csscolumns img { margin: 3px 8px 3px 0; float: left; } .columns img规则。两个规则有着相同的特殊性,因此,如果它们顺序颠倒,则.columns img的10像素的左边距可能会覆盖你刚刚创建的新规则。你可以将.no-csscolumns img重命名为.no-csscolumns .columns img以便于给它更高的特殊性,但是最好是保证selector越简单越好。(顺便说一下,如果你不确定什么是特殊性,查阅Adrian Senior的文章,Understanding Specificity。它虽然发表时间很长,但是很有价值。)在这个的简单范例中,我只用了前缀为no-的类就为旧版本的浏览器创建了特殊的样式。 但是,按照它们的能力,你不同时使用这两个类(带或不带前缀)为浏览器创建不同的样式是绝对没有道理的。 例如:
.csscolumns { /* rules for browsers that support multi-column layout */ } .no-csscolumns { /* rules for browsers that don't support multi-column layout */ } 有时这种方法是合理的,例如,如果你想为每一级支持都创建一个完全不同的布局。 但是如果它仅仅是一个为旧版本浏览器提供可选择的样式的问题,不要忘记,浏览器会忽略它们无法识别的属性。 如果你对所有的样式都使用 Modernizr 类,那么在 JavaScript 禁用的浏览器中你的页面将完全非样式化。
Modernizr 为开始的<html>标签添加的类名称起着双重目的作用。当页面加载时,它们也是 Modernizr 对象创建的 JavaScript 属性的名称。表1列举了与 CSS 有关的类和属性的名称。表2列举了剩下的与 HTML5 和相关技术有关的类和属性,例如地理位置。
表2.Modernizr 检测的与 HTML5 有关的功能
HTML5 有关功能 | Modernizr 类(属性) |
Application cache | applicationcache |
Audio | audio.type(ogg, mp3, wav, m4a) |
Canvas | canvas |
Canvas text | canvastext |
Drag and drop | draganddrop |
Form input attributes | input.attributeName |
Form input elements | inputtypes.elementName |
Geolocation | geolocation |
hashchange event | hashchange |
History management | history |
IndexedDB | indexeddb |
Inline SVG | inlinesvg |
Local storage | localstorage |
Messaging | postmessage |
Session storage | sessionstorage |
SMIL | smil |
SVG | svg |
SVG clip paths | svgclippaths |
Touch events | touch |
Video | video.type(ogg, webm, h264) |
WebGL | webgl |
Web sockets | websockets |
Web SQL database | websqldatabase |
Web workers | webworkers |
在大多数情况下,表1和表2列举的所有属性返回的都是true或者false。所以,你可以按照如下所示使用 JavaScript 对本地存储进行测试:
if (Modernizr.localstorage) { // script to run if local storage is supported } else { // script to run if local storage is not supported } 然而,就audio和video而言,返回值是一个字符串,它表明着浏览器能够处理特定类型的置信水平。 根据 HTML5 规范,空的字符串表示该类型不支持。 如果支持该类型,那么返回值是maybe或是probably。 例如:
if (Modernizr.video.h264 == ) { // h264 is not supported } HTML5 添加了许多新的表单属性,例如autofocus,当页面第一次加载时它会自动地将光标放在某个指定的字段。 另一个有用的属性是required, 如果某个必需的字段留有空白,那么它将阻止HTML5兼容的浏览器提交表单(参见图6)。
这很好,但是它会给你留下一个问题:旧版本的浏览器该怎么办?
一个解决方法是忽略它们,并留给服务器侧的验证功能进行最终的检查。 如果浏览器无法识别required属性,那么另外一个处理这种情况的用户界面更为友好的方法是创建一个小小的脚本对必需字段进行检查。 如下的指令显示了在Modernizr帮助下如何进行相应的操作。
</style> <script src=js/modernizr.js></script> </head> <script>块,并且在页面一加载完就创建一个事件处理程序以便于执行代码:<script src=js/modernizr.js></script> <script> window.onload = function() { // code to execute when page loads }; </script> </head autofocus和required属性的浏览器中模拟它们。处理autofocus的方式很简单:window.onload = function() { // get the form and its input elements var form = document.forms[0], inputs = form.elements; // if no autofocus, put the focus in the first field if (!Modernizr.input.autofocus) { inputs[0].focus(); } // if required not supported, emulate it } 该条件测试了Modernizr.input.autofocus,如果不支持autofocus,那么返回的值是false。 然而,逻辑运算符NOT(一个感叹号)却能使意思颠倒,因此如果不支持autofocus,那么该条件的求值结果为true,并且inputs[0].focus()将光标放在第一个输入字段。
required,那么现在添加代码以便于检查必需字段。 事件处理程序的完整代码如下所示:window.onload = function() { // get the form and its input elements var form = document.forms[0], inputs = form.elements; // if no autofocus, put the focus in the first field if (!Modernizr.input.autofocus) { inputs[0].focus(); } // if required not supported, emulate it if (!Modernizr.input.required) { form.onsubmit = function() { var required = [], att, val; // loop through input elements looking for required for (var i = 0; i < inputs.length; i++) { att = inputs[i].getAttribute('required'); // if required, get the value and trim whitespace if (att != null) { val = inputs[i].value; // if the value is empty, add to required array if (val.replace(/^/s+|/s+$/g, '') == '') { required.push(inputs[i].name); } } } // show alert if required array contains any elements if (required.length > 0) { alert('The following fields are required: ' + required.join(', ')); // prevent the form from being submitted return false; } }; } } 新代码产生了一个函数,当提交表单时它能够遍历所有的输入元素,以便于找到具有required属性的字段。 当它找到某个字段时,它会从值中除去开头和结尾的空白,并且如果结果是一个空的字符串,那么它会把结果添加到required数组中。 在所有的字段都已经得到检查后,如果数组中包含某些元素,那么浏览器会显示一个与缺失字段名称有关的警告,并阻止提交表单。
注意:Safari 5.1 错误地报告了它支持required属性,所以它在没有验证必需字段的情况下就提交了表单。 这是 Safari 的缺陷,然而在 Modernizr 里它是不存在的。
当你准备好对你的网站进行部署时,推荐创建一个 Modernizr 的自定义 production 版本,它只包含那些你实际需要的元素。 这可以按照你所选的功能将 Modernizr 库的大小从 44KB 缩小到 2KB。当前选项的范围如图8所示。
图8. Modernizr 下载页面允许你仅选择那些你需要的功能选项可以便捷地按照如下类别进行分组:CSS3、HTML5、Misc(ellaneous) 和 Extra。 单击前三个标题旁边的 Toggle 按钮可任意地选择或者放弃选择分类中的所有复选框。
在默认情形下,Extra 分类将会选中如下三个条目:
如果你在 CSS3 分类中选中任何选项,那么如下 Extra 分类中的选项也会被选中:
Modernizr.testProp()Modernizr.testAllProps()Modernizr._domPrefixes()不要取消选择这些选项。 这样做将会自动地取消你在 CSS3 分类中已经选择的任何选项。
Extra 分类中的MQ Polyfill (respond.js) 添加了一个脚本,它可以使IE 6-8中的 media queries 获得有限的支持。当你选中这个选项时,它会自动地选中Media Queries 和 Modernizr.testStyles()。如需知道更多关于media queries polyfill (respond.js)的信息,请访问https://github.com/scottjehl/Respond。
只有高级用户才会对 Extra 分类中的其它选项感兴趣。关于它们是什么和如何使用它们的更多细节,参见Modernizr 文档的扩展性部分(Extensibility section of the Modernizr documentation)。
下面的说明描述了如何为范例文件创建一个 Modernizr 的自定义production版本。 后续的练习需要使用这一自定义版本,它将展示如何使用Modernizr.load()加载外部 JavaScript 文件。
当创建Modernizr的自定义production版本时,在默认情形下,必须选中包含Modernizr.load()的选项。Modernizr.load()是yepnope()的别名,它是与 Modernizr 同步开发的独立脚本加载器。 为了说明如何使用它,我给出一个简单范例。我已经将相应的脚本从 required.html 移到了 check_required.js,并且做了三个微小的改动以便于去除 Modernizr 测试以及将它赋值到一个名为init的变量中。 修订的脚本如下所示:
var init = function() { // get the form and its input elements var form = document.forms[0], inputs = form.elements; // put the focus in the first input field inputs[0].focus(); // check required fields when the form is submitted form.onsubmit = function() { var required = [], att, val; // loop through input elements looking for required for (var i = 0; i < inputs.length; i++) { att = inputs[i].getAttribute('required'); // if required, get the value and trim whitespace if (att != null) { val = inputs[i].value; // if the value is empty, add to required array if (val.replace(/^/s+|/s+$/g, '') == '') { required.push(inputs[i].name); } } } // show alert if required array contains any elements if (required.length > 0) { alert('The following fields are required: ' + required.join(', ')); // prevent the form from being submitted return false; } }; }; Modernizr.load()的一个很大的优点是,根据测试浏览器能力的结果,它可以有条件地加载脚本—这就是为什么起初叫它yepnope()的原因。 它可以异步地加载外部脚本—换句话说,就是能够在浏览器已加载 Document Object Model (DOM) 之后加载外部脚本—因此它可以有助于提升你的网站性能。
Modernizr.load()的基本语法是将一个具有如下属性的对象传递给它:
test: 你希望检测的 Modernizr 属性。yep: 如果测试成功,你希望加载的脚本的位置。 使用一个多脚本数组。nope: 如果测试失败,你希望加载的脚本的位置。 使用一个多脚本数组。complete: 外部脚本一经加载就运行的函数(可选)。yep和nope两者都是可选的,只要你提供了其中一个即可。
为了在 check_required.js 中加载和执行脚本,需要在 modernizr.adc.js 已附着到页面之后添加如下<script>块(代码位于required_load.html 中):
<script> Modernizr.load({ test: Modernizr.input.required, nope: 'js/check_required.js', complete: function() { init(); } }); </script> 这样就与之前运行的完全一致,但是却可以降低已支持required属性的浏览器的下载负荷。
为了测试多种条件,你可以给Modernizr.load()传递一组对象。如需获得更多细节信息,参见 Modernizr 文档上的Modernizr.load() 教程。
Modernizr 是一个强大而有用的工具,但是这并不意味着你就应该使用它。 并不是在所有情形下均必须使用 Modernizr 给浏览器提供多种样式。 如果你主要关注的对象是 Internet Explorer,那么考虑使用IE conditional comments。 你也可以使用CSS层叠覆盖一些样式。 例如,先使用hexadecimal color,然后使用rgba()或hsla()覆盖它。 旧版本的浏览器会使用第一个值并且忽略第二个值。
Modernizr 真正地变成现实是当它与 polyfills 和其它 JavaScript 相结合的时候。但是记住,通常很容易创建属于你自己的适合支持功能的测试。例如,下面就是你测试某个浏览器是否支持required属性的全部代码(代码位于required_nomodernizr.html 中):
var elem = document.createElement('input'); if (typeof elem.required != 'boolean') { // required is not supported } 本教程已经涵盖了 Modernizr 的所有主要功能 如需了解关于这些功能的更多信息,请查阅相应的官方文档,其网站地址为http://www.modernizr.com/docs/。 此外,你还可以找到下列有用资源:
yepnope()的深入讨论,它已经在 Modernizr 中被合并为Modernizr.load()。+