浏览器和功能检测:让您的网站在任何地方都看起来很棒






4.78/5 (9投票s)
当您构建一个网站时,您希望它在任何浏览器中看起来都很棒……而且理想情况下,它应该在很长一段时间内都看起来很棒,甚至在未来的浏览器版本中也是如此。我收集了一些技巧、窍门和最佳实践,将帮助您的网站呈现最佳效果。
当您构建一个网站时,您希望它在任何浏览器中看起来都很棒……而且理想情况下,它应该在很长一段时间内都看起来很棒,甚至在未来的浏览器版本中也是如此。我收集了一些技巧、窍门和最佳实践,将帮助您的网站呈现最佳效果。
一点历史
如今,所有 Web 浏览器都以一个共同的目标为宗旨:根据最新的规范最佳地渲染网页。
过去并非如此。过去,大多数浏览器都实现了尚未标准化的热门功能。每个浏览器都以自己的方式实现,例如,在 CSS 中设置透明度的可能性。
Internet Explorer(8 版本之前)理解以下 CSS:
.transparent {
/* Internet Explorer < 9 */
width: 100%;
filter: alpha(opacity=50);
}
Firefox 有自己的属性:
.transparent {
/* Firefox < 0.9 */
-moz-opacity:0.5;
}
Safari 也是如此:
.transparent {
/* Safari < 2 */
-khtml-opacity: 0.5;
}
然而,在 CSS3 中,现在有一种统一的方式来设置元素的透明度:
.transparent {
/* IE >= 9, Firefox >= 0.9, Safari >= 2, Chrome, Opera >= 9 */
opacity: 0.5;
}
当浏览器“额外努力”快速支持新功能时,这无疑是一件好事。但是,当这些功能未标准化时,开发人员的生活确实会变得更加艰难,因为我们必须考虑每个功能的各种实现。
相同的标记
确保您的网页在所有浏览器中都能最佳渲染的最佳方法是使用所有当前浏览器都支持的标记。直到最近,这还是 HTML 4.01,一个功能非常有限的 10 年历史的标准。
如今,所有浏览器都趋向于功能丰富的 HTML5。然而,“HTML5”一词涵盖的许多新规范(包括 HTML5 标记、其 API,如 DOM Levels 2 和 3、CSS3、SVG 和 EcmaScript 262)仍处于开发中,并且可能会发生变化。
浏览器供应商正在不断添加对 HTML5 功能的支持,但速度差异很大。
Firefox 和 Opera 通常会快速采用新的 HTML5 规范,即使是一些处于早期开发阶段、可能发生变化或存在安全问题的规范。
从好的方面来看,开发人员有机会测试新功能是一件很棒的事情。然而,这种早期采用率通常会导致网站使用会破坏浏览器版本之间页面功能的特性。一个例子是 Firefox 4 因安全原因在 Beta 7 和 8 之间禁用了 Websockets。这对用户和开发人员来说都是一种极其令人沮丧的体验。
Chrome——也正在快速采用新的 HTML5 标准——最近以其 声明 放弃对 HTML5 <video> 元素的流行 h.264 视频编解码器的支持,转而使用免费的 WEBM 标准,从而引起了 HTML5 社区的关注。虽然这对于目前为 h.264 许可证付费的开发人员来说并非坏事,但它又增加了更多的选择,开发人员需要跟踪他们需要编码和托管内容的视频格式,才能支持尽可能多的浏览器。
Microsoft 不会像其他公司那样快速实现标准,但它会与 W3C 密切合作来构建测试套件。这种合作有助于
- 最小化规范中的歧义
- 为浏览器供应商提供技术基础
- 并统一浏览器如何渲染 HTML5。
要了解他们在此领域的最新工作,请查看可在 IE Test Drive 上找到的 Internet Explorer 10 平台预览版。
您还可以查看 HTML5labs,Microsoft 在此发布 Web 标准机构(如 W3C)的早期不稳定规范原型。有关 Internet Explorer 9 当前如何支持各种 HTML5 规范的深入信息,请参阅 通过标准支持改进的互操作性。
但是,由于新的 HTML5 标准仍然是一个不断变化的目标——而大多数 Internet 用户并不使用任何给定浏览器的最新版本——因此提供正确的标记比以往任何时候都更重要。
浏览器检测
处理浏览器差异的一种方法是使用浏览器检测。最常见的方法是使用 JavaScript 查询用户代理标头。
<script type="text/javascript"> if ( navigator.userAgent.indexOf("MSIE")>0 ) { // run custom code for Internet Explorer. } </script>
但是,这样做存在一些问题。
首先,它将对浏览器支持哪些功能的多个假设捆绑在一个单一的检查中。一个错误的假设可能会导致网站瘫痪。因此,作为开发人员,您必须跟踪哪些功能由哪些浏览器支持。
第二个问题是上面的浏览器检查未考虑浏览器版本,因此它不是面向未来的。因此,即使它适用于浏览器当前的版本,下一个版本也可能不需要——或者更糟的是,可能完全删除对用于向网站添加浏览器检测的解决方案的支持。
如果您必须使用浏览器检测,请确保仅将其用于检测旧版浏览器,如下例所示,并始终牢记您正在检查的浏览器版本。
<script type="text/javascript"> function getInternetExplorerVersion() // Returns the version of Internet Explorer or a -1 for other browsers. { var rv = -1; if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) rv = parseFloat( RegExp.$1 ); } return rv; } function onLoad() { var version = GetInternetExplorerVersion() if (version <= 7 && version > -1) { // Code to run in Internet Explorer 7 or less. } } </script>
如果您有兴趣,MSDN 有一篇很棒的文章提供了更多信息:“更有效地检测浏览器”。
JavaScript 教程 有一篇关于如何使用 navigator 对象和正则表达式来检测各种浏览器及其确切版本的全面文章。
检测浏览器的另一种方法是使用 Internet Explorer 的条件注释。此语法扩展了标准的 HTML 注释,并且自 5 版本以来是 Internet Explorer 特有的。
条件注释的一种用法是与 CSS 样式表结合使用。您可以设置某些仅适用于 IE 的 CSS 规则——这些规则是您希望其他浏览器忽略的。在以下示例中,“ie7.css”仅在检测到 Internet Explorer 7 或更低版本时加载。
<!--[if lte IE 7]> <style TYPE="text/css"> @import url(ie7.css); </style> <![endif]-->
您可以在 MSDN 上阅读有关条件注释所有功能的更详细信息:“关于条件注释”。
考虑到浏览器检测的所有问题和限制,让我们来看一个替代方案。
特性检测
处理您页面中 Web 浏览器差异的更好方法是使用特性检测。
在使用您知道在不同浏览器中有不同实现的特性之前,您可以运行一个小的测试,查找特定对象、方法、属性或行为的可用性。
在大多数情况下,可以通过尝试创建所讨论特性的新实例来完成此操作。如果该实例化返回的值不是 null,则执行的浏览器了解该特性。如果不是,您可以测试是否有可用的解决方法或专有的旧实现。
比较浏览器检测和特性检测
有时图表有助于阐明在各种情况下两者之间的区别。
这些是我们测试网站的所有可能的代码路径。
众所周知的浏览器配置
在面对众所周知的浏览器配置时,两者都有效。但是浏览器检测存在一个固定的假设,即特性 A 和特性 B 都受浏览器支持,而特性检测分别测试每个特性。
未知的浏览器配置
当面对未知的浏览器配置时,情况会变得非常有趣。
特性检测可以很好地处理问题,并发现该浏览器能够显示特性 A,但需要为特性 B 提供备用代码。浏览器检测选择一个路径,因为它只检查浏览器的名称,或者当没有查询到的浏览器/版本组合匹配时,它简单地选择默认路径。
无论哪种方式,在这种情况下,页面都无法通过浏览器检测正确渲染,因为没有代码路径连接所有有效的代码段,尽管页面会包含所有必要代码来在此未知浏览器配置中正确显示。
特性检测示例
在使用特性检测时,需要牢记两个非常重要的建议。
第一,始终测试标准,因为浏览器通常同时支持较新的标准和旧的解决方法。
第二,在一次检查中仅定位相关的特性,以最小化对浏览器功能的假设。
让我们看几个特性检测的例子。
以下脚本创建了两个代码路径。它首先检查浏览器是否支持 window.addEventListener,如果不支持,则探测旧特性 window.attachEvent 的可用性。
<script type="text/javascript"> if(window.addEventListener) { // Browser supports "addEventListener" window.addEventListener("load", myFunction, false); } else if(window.attachEvent) { // Browser supports "attachEvent" window.attachEvent("onload", myFunction); } </script>
另一种好方法是将特性检测封装到一组函数中,这些函数随后可以在代码的任何地方使用。这是一个关于如何检测浏览器是否支持 HTML5 <canvas> 的最佳实践,如果支持,则确保 canvas.getContext(‘2d’) 方法也能正常工作。它简单地返回“true”或“false”,使其易于重用。
<script type="text/javascript"> function isCanvasSupported() { var elem = document.createElement('canvas'); return !!(elem.getContext && elem.getContext('2d'); } </script>
在使用特性检测时,始终在 newly created 元素或对象上使用它。这有助于排除页面上的任何其他脚本自创建以来修改它的可能性,这可能导致随机或不稳定的结果。
特性检测也可以直接用于少数 HTML 元素,例如 HTML5 的 <video>、<audio> 和 <canvas>,形式为“备用”。浏览器显示从顶部开始的第一个受支持的子元素,并在视觉上隐藏下面的元素。
最简单的形式如下所示:
<video src="video.mp4"> Your browser doesn't support videos natively. </video>
支持 <video> 元素的浏览器将显示视频“video.mp4”,而不支持的浏览器将回退到提供的文本。但是,备用功能也适用于 video 标签中的各种视频格式。
<video> <source src="video.mp4" type="video/mp4" /> <source src="video.webm" type="video/webm" /> Your browser doesn’t support videos natively. </video>
在这种情况下,支持 HTML5 <video> 的浏览器将首先尝试加载 mp4 视频。如果它不支持此格式,它将回退到 webm 编码的视频。如果该格式也不受支持,或者浏览器根本不支持 <video>,它将回退到文本。
当然,而不是这些文本,如果浏览器根本不支持 HTML5 <video>,回退到基于插件的视频播放器会更有意义。以下示例使用了 Silverlight 视频播放器:
<video> <source src="video.mp4" type='video/mp4' /> <source src="video.webm" type='video/webm' /> <object type="application/x-silverlight-2"> <param name="source" value="http://url/player.xap"> <param name="initParams" value="m=http://url/video.mp4"> </object> Download the video <a href="video.mp4">here</a>. </video>
非常相似的逻辑也适用于 CSS。在 CSS 中,任何未识别的属性都会被简单地忽略。因此,当您想要添加多个、实验性的、带厂商前缀的属性时,如下面的“border-radius”所示,您可以简单地将所有变体包含在您的代码中。这可能感觉有点不精确,但对于这种情况来说,它易于使用且能完成任务。
<style type="text/css"> .target { border-radius: 20px; -moz-border-radius: 20px; -webkit-border-radius: 20px; } </style>
特性检测的一个巨大优势是它适用于您在创建页面时未曾想到的浏览器。而且因为它不依赖于任何基于浏览器的假设,所以它适用于未来版本的浏览器。
开发和测试特性检测
当您准备好在多种不同浏览器上开发和测试特性检测时,您会想要查看 Internet Explorer 9 中的 F12 开发人员工具。您可以使用它来逐步调试脚本,更改浏览器的用户代理字符串,并指示 Internet Explorer 使用先前版本的渲染引擎。
在大型项目中管理特性检测
在创建复杂的 Web 项目时,创建和管理所有特性检测代码本身可能很繁琐。
幸运的是,有一些很棒的 JavaScript 库可用,它们有助于此工作,即 Modernizr 和 jQuery。
Modernizr 已经内置了对大多数 HTML5 和 CSS3 功能的检测,这些功能在您的代码中非常易于使用。Modernizr 得到了广泛采用并不断得到增强。Modernizr 和 jQuery 都随 ASP.NET MVC 工具一起提供。
看看下面的代码来检测浏览器显示 Web 字体的能力。
没有 Modernizr | 有 Modernizr |
function(){
var
sheet, bool,
head = docHead || docElement,
style = document.createElement("style"),
impl = document.implementation || { hasFeature: function()
{ return false; } };
style.type = 'text/css';
head.insertBefore(style, head.firstChild);
sheet = style.sheet || style.styleSheet;
var supportAtRule = impl.hasFeature('CSS2', '') ?
function(rule) {
if (!(sheet && rule)) return false;
var result = false;
try {
sheet.insertRule(rule, 0);
result = (/src/i).test(sheet.cssRules[0].cssText);
sheet.deleteRule(sheet.cssRules.length - 1);
} catch(e) { }
return result;
} :
function(rule) {
if (!(sheet && rule)) return false;
sheet.cssText = rule;
return sheet.cssText.length !== 0 &&
(/src/i).test(sheet.cssText) &&
sheet.cssText
.replace(/\r+|\n+/g, '')
.indexOf(rule.split(' ')[0]) === 0;
};
bool = supportAtRule('@font-face { font-family: "font";
src: url(data:,); }');
head.removeChild(style);
return bool;
};
|
<script type="text/javascript" src"modernizr.custom.89997.js"></script> <script type="text/javascript"> if (Modernizr.fontface){ // font-face is supported } </script> |
“救命!”当浏览器不支持您需要的特性时该怎么办
如果测试的浏览器不支持您需要的特性,特性检测不会为您承担找出解决方法的工作。抱歉。
在上面的 HTML5 视频示例中,使用 Silverlight 作为备用方案是一个显而易见的选择。但是对于其他 HTML5 特性,例如 <canvas> 或 HTML5 中的新语义标签,包括 <nav>、<section> 和 <article>、<aside> 或新的 <header> 和 <footer> 呢?
对于大多数 HTML5 特性,有越来越多的现成“备用方案”,称为 shims 和 polyfills。 Shims 和 polyfills 是 CSS 和 JavaScript 库(有时是 Flash 或 Silverlight 控件),您可以将它们添加到您的项目中,它们将添加缺失的、不支持的 HTML5 特性。
总体的想法是,开发人员应该能够使用 HTML5 API 进行开发,并且脚本可以创建应该存在的方法和对象。以这种面向未来的方式进行开发意味着,随着用户升级,您的代码无需更改——用户将无缝过渡到更好的原生体验。
Shim 和 polyfill 之间的区别在于,shim 只模仿特性,但有自己的专有 API。Polyfill 同时模拟它所替换的 HTML5 特性的功能和确切的 API。所以,总的来说,使用 polyfill 可以省去您采用专有 API 的麻烦。
GitHub 上的 HTML5 Cross Browser Polyfills 集合包含一个不断增长的可用 shims 和 polyfills 列表。
例如,Modernizr 包含用于语义标签支持的“HTML5Shim”,但如果 Modernizr 检测到某个特性不受支持,加载其他 shims/Polyfills 会很容易。
“添加所有这些脚本库不会使我的页面变得庞大且加载缓慢吗?”
很高兴您问到!确实,添加许多这些支持库可能会为您的网站增加大量开销。这就是为什么您不应该做得过火——在需要时动态加载它们。对于 shim 或 polyfill,最佳实践是仅在您检测到浏览器确实需要它,因为它不支持本地 HTML5 特性时才加载它们。
再次,您很幸运!有一个很棒的库支持这种情况:yepnope.js
它是一个异步“资源加载器”,可同时用于 JavaScript 和 CSS,并完全将预加载与执行分离。这意味着您可以完全控制何时执行资源,并且可以即时更改顺序。Yepnope 将集成到 Modernizr 2 中,但也可以单独使用。
让我们看看 yepnope 的语法。
<script type="text/javascript" src="yepnope.1.0.2-min.js"></script> <script type="text/javascript"> yepnope({ test : Modernizr.geolocation, yep : 'normal.js', nope : ['polyfill.js', 'wrapper.js'] }); </script>
此示例将测试浏览器使用 Modernizr 进行 HTML5 地理定位的能力。如果支持,将加载您自己的代码 (normal.js),否则将加载自定义 polyfill(由 polyfill.js 和 wrapper.js 组成)。
总结:该做什么和不该做什么
最后,让我们总结一下最重要的几点。
该做什么 | 不该做什么 | |
浏览器检测 | 尽量避免 或者 测试特定浏览器 AND version |
对未来的浏览器进行假设 仅通过测试浏览器名称。 |
特性检测 | 先测试标准 | 在一个测试中假设不相关的特性。 |
在以下位置了解更多关于浏览器和特性检测的信息:
- Internet Explorer Test Drive
- HTML5 实验室
- Internet Explorer 展示“Web 之美”
- MSDN 文章
- JavaScript 库
- Polyfills / Shims
- GitHub 上的 HTML5 Cross Browser Polyfills 列表