iOS 到 IE10 Metro:构建跨浏览器无插件体验
在本文中,我们将处理 MSNBC 的无插件富媒体体验。这可以分解为两点:样式和脚本。
如果您已经为 iPad 构建了无插件浏览体验,只需进行一些更改,它就可以准备好支持 Windows 8 上新的 IE10 无插件体验。随着越来越多的浏览器采用无插件方法,现在是时候开始考虑它了。我将在下面的几步中向您展示如何通过编写适用于所有现代浏览器的代码来实现这一点。
今天,我们将处理 MSNBC 的 无插件富媒体体验。这可以分解为两点:样式和脚本。
要修改 MSNBC 的文件,我将使用一个名为 Fiddler 的代理应用程序。您可以从 http://fiddler2.com 下载此工具。此工具允许我像修改本地计算机上的文件一样修改远程文件。如果您可以直接访问自己的网站,则可以忽略 Fiddler,直接处理您的文件。Fiddler 提供了一种测试更改而不会破坏实时站点的绝佳方法。
第一步:为现代浏览器声明标准模式和有效标记
为了使用我们将在下面使用的 HTML5 元素,您首先需要确保您正在以标准模式运行。确保此项的一种方法是在文档的顶部包含 HTML5 doctype
<!DOCTYPE html>
第二步:更新您的 CSS 供应商前缀
CSS 语言不断变化,新功能被建议、更新和标准化。为了让开发人员学习如何使用这些新功能,浏览器供应商通常会通过前缀属性提供实验性实现。
负责任地使用供应商前缀的关键部分是确保您的网站包含每个供应商的前缀,以支持最广泛的功能。在许多情况下,尤其是在构建以 iPad 为中心的网站时,您可能只关注 -webkit
属性,而忽略了针对其他浏览器的前缀,例如 -o, -ms, and -moz
。其最终结果是,您极大地限制了能够呈现您的无插件站点的目标设备,并为使用其他现代浏览器的用户提供了降级体验,其中许多浏览器可以提供同样引人入胜的功能。
例如,我们在 MSNBC 上发现以下 内容
background: -webkit-gradient( linear, left top, left bottom, color-stop(1, rgba(192,192,192,.6)), color-stop(0.5, rgba(0,0,0,.6)) );
随着 HTML5 无插件体验的趋势日益增长,为其他主要浏览器提供供应商前缀也很重要。
background: -webkit-linear-gradient( top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% ); background: -moz-linear-gradient( top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% ); background: -ms-linear-gradient( top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% ); background: -o-linear-gradient( top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% ); background: linear-gradient( top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
虽然更冗长,但广泛的浏览器功能支持带来的好处肯定 outweighs 了涉及的额外打字工作。此外,还有许多优秀的工具可以减轻这项工作量,例如 SASS 和 Compass、-prefix-free,甚至即将推出的 Visual Studio 2011 中的 CSS Snippets。
另外,如果您主要使用 JavaScript 并且想节省确定客户端浏览器支持哪些功能的时间,请参阅 IEBlog 上 “使用供应商前缀进行编程的最佳实践” 中的说明。
第三步:摆脱浏览器嗅探方法
有两种方法用于确定用户的浏览器和设备的能力。一种方法是 浏览器嗅探
,它不幸地相当流行。此方法包括检查 navigator 对象以查找特定模式或值。
if ( navigator.userAgent.indexOf("iPad") > -1 ) {
// Load HTML5 Experience
} else {
// Load Flash Experience
}
上面的代码检查用户代理字符串是否包含“iPad”,如果找到则提供无插件 HTML5 体验。否则,假定您使用的是安装了 Flash 的设备。对于禁用了插件但其浏览器能够处理 HTML5 功能的非 iPad 用户,这将导致体验中断。
这里有一个尝试查找 Internet Explorer 版本的示例。
if ( tests.IE ) {
j = /msie.(\d\.\d+)/i;
k = navigator.userAgent.match(j)[1];
}
用户代理字符串被测试为一个试图定位版本号的模式。此模式查找单个数字,后跟一个句点,后跟任意数量的其他数字。虽然此测试可以找到“MSIE 8.0”和“MSIE 9.0”之类的值,但它无法识别最新版本的 Internet Explorer,因为它将自身标识为“MSIE 10.0”,因为句点前只期望一个数字。
这些只是浏览器嗅探不是最佳实践的几个例子。用户代理字符串并非不可更改 — 它是可读写的值,很容易被插件甚至用户更改。大多数现代浏览器都允许从其开发工具中轻松更改此值,一些用户会利用这一点来绕过开发不佳的网站。
如果我们禁用插件,或者从没有 Flash 的设备/浏览器访问 MSNBC,我们应该期望它会尝试提供无插件体验。不幸的是,事实并非如此。我们没有看到 HTML5 体验,而是被要求下载 Flash。这是因为该网站将用户分为两类:iPad 用户或启用 Flash 的用户。
特性检测
与其尝试通过嗅探用户代理字符串来猜测浏览器功能(这最终会让你失败),不如直接在浏览器中测试功能要明智得多。如果您想测试浏览器通过 HTML5 提供视频和音频的能力,您可以尝试通过 JavaScript 创建这些元素,然后查看浏览器是否能理解它们。这种做法称为 功能检测
。
if ( !!document.createElement("video").canPlayType ) {
// Load HTML5 Video
} else {
// Load Flash Video
}
在上面的示例中,我们首先测试我们新创建的 video
标签上是否存在 canPlayType
方法。我们使用双重否定将响应强制转换为布尔值。如果浏览器理解 video
元素是什么,那么 canPlayType
方法就会存在。如果浏览器不认识 video 元素,那么 canPlayType
方法将不存在。如果此测试通过,我们将加载我们的 HTML5 视频。如果测试不通过,我们将尝试加载 Flash。这里可以进行更深入的功能检测,因为机器上可能没有 Flash,或者 Flash 可能被禁用。
功能检测是确定浏览器功能的首选方法,因为它没有任何猜测。如果浏览器通过正确构造的测试,那么它绝对支持您想要使用的功能。
有许多出色的工具可以为您提供功能测试。其中一个工具提供了 40 多个测试,那就是 Modernizr。此工具创建一个名为“Modernizr
”的全局对象,其中包含您的测试结果。使用 Modernizr,测试 HTML5 视频支持非常简单
if ( Modernizr.video ) {
// Load HTML5 Video
}
MSNBC 使用浏览器嗅探来判断访问页面的设备是否为 iPad。我们的第一步是删除浏览器嗅探代码,并用功能检测代码替换它。
在修改浏览器嗅探代码之前,我们首先需要找到它。在 Internet Explorer 中,按 F12 会弹出我们的 开发人员工具。在工具中,打开“脚本”选项卡并搜索“userAgent”。此搜索将查找站点所有脚本文件中此属性名称的任何实例。我们对来自 http://www.msnbc.msn.com/id/37156949/ 第 41 行的结果感兴趣。
现在我们知道要编辑什么了,我们可以打开 Fiddler 并加载我们的流量。一旦 Fiddler 打开,对 MSNBC 页面执行硬刷新(在 IE 中按 Ctrl+F5)。这将导致所有页面会话在 Fiddler 中列出。
仔细查看,您会注意到我们的资源是顶部的第三个。接下来,我将为这个会话文件设置一个 自动响应器,以便每次请求它时,都会用我自己的自定义文件替换服务器响应。
- 右键单击此会话,然后从上下文菜单中选择“解码选定的会话”。
- 选择右侧的“自动响应器”选项卡。
- 在“自动响应器”选项卡中,选中“启用自动响应”复选框。
- 将左侧面板中的选定会话拖到“自动响应器”选项卡中。
此时,您的“自动响应器”选项卡中应有一个条目,其中包含以下规则:
- 如果 URI 匹配:EXACT:http://www.msnbc.msn.com/id/37156949/
- 然后响应:*200-SESSION_3
右键单击“自动响应器”中的条目,然后选择 编辑响应
。在随后的弹出窗口中,切换到“SyntaxView”选项卡,在那里我们将找到此文件的来源。如预期所示,第 41 行包含我们的浏览器嗅探代码。
if(!(navigator.userAgent.toLowerCase().indexOf("ipad")>-1)){
// Flash Experience
}
我们不测试 userAgent 的内容,而是查找对 HTML5 video 标签的支持。将上述条件切换为以下内容:
if ( !document.createElement("video").canPlayType ) {
// Flash Experience
}
此测试检查我们是否无法使用 video 元素。如果 canPlayType 返回 undefined,它将被强制转换为 true,然后进入第一个代码块,设置 Flash 体验。
第四步:更新触摸和指针事件
Safari 同时支持触摸事件模型和鼠标事件模型。Internet Explorer 10 将触摸、鼠标和触笔事件组合成一个称为 指针
的单一抽象项。事实上,Internet Explorer 10 是第一个适用于所有设备上所有输入类型的浏览器。这种抽象大大减少了确定您应该绑定到哪个事件模型以及如何检测用户交互所需的努力。然后通过 MSPointer 事件处理此指针。如有必要,您可以通过访问 pointerType
属性来确定指针的类型。
鉴于 Internet Explorer 不支持 Apple 的专有事件模型,包括 touchstart, touchmove
和 touchend
等触摸事件,MSNBC 的事件侦听器需要修改为监听 MSPointer 事件,例如 MSPointerDown, MSPointerUP
和 MSPointerMove
。
由于事件模型实现的差异,请使用 Modernizr 等功能检测工具或类似代码来定位所有主要的事件模型。
if (window.navigator.msPointerEnabled) {
myCanvas.addEventListener("MSPointerMove", paint, false);
} else {
myCanvas.addEventListener("mousemove", paint, false);
myCanvas.addEventListener("touchmove", paint, false);
}
MSNBC 只支持触摸事件,我们需要对其进行更改,以便使用鼠标的访问者仍然可以与页面进行交互。
我们的事件链接在 http://www.msnbc.msn.com/id/43662671/15。
document.addEventListener("touchstart", touchHandler, false);
document.addEventListener("touchmove", touchHandler, false);
document.addEventListener("touchend", touchHandler, false);
我们将更新此内容以包含 MSPointer 事件。
if (window.navigator.msPointerEnabled) {
document.addEventListener("MSPointerDown", touchHandler, false);
document.addEventListener("MSPointerMove", touchHandler, false);
document.addEventListener("MSPointerUp", touchHandler, false);
} else {
document.addEventListener("touchstart", touchHandler, false);
document.addEventListener("touchmove", touchHandler, false);
document.addEventListener("touchend", touchHandler, false);
document.addEventListener("mousedown", touchHandler, false);
document.addEventListener("mousemove", touchHandler, false);
document.addEventListener("mouseup", touchHandler, false);
}
首先,我们检查指针是否存在。由于 MSPointer 覆盖鼠标、手指和触笔,因此除了它们之外我们不需要其他东西。如有必要,我们回退以同时提供触摸和鼠标事件。
接下来,我们需要在 http://www.msnbc.com/id/44937131/ 中为这些事件类型创建案例。目前,MSNBC 从以下开始:
if ( event.type == "touchstart" ) {
/* Start drag logic */
} else
if ( event.type == "touchmove" ) {
/* Drag logic */
} else
if ( event.type == "touchend" ) {
/* Complete drag logic */
}
我们将修改它以监听所有注册的事件类型。
if ( event.type.match( /(down|start)$/i ) ) {
/* Start drag logic */
} else
if ( event.type.match( /move$/i ) ) {
/* Drag logic */
} else
if ( event.type.match( /(up|end)$/i ) ) {
/* Complete drag logic */
}
上面的代码使用 match 方法和一系列正则表达式来确定引发了哪个事件。如果引发的事件以不区分大小写的“down”或“start”结尾,我们将开始拖动代码。如果事件以不区分大小写的“move”结尾,我们将执行实际的拖动逻辑。最后,如果事件以不区分大小写的“up”或“end”结尾,我们将结束拖动事件。注意:其他事件也可能在此处捕获,例如 onresizeend
和 keyup
。请确保在您的项目中考虑这一点。
上面的代码实现的是 Ted Johnson 在 “处理所有浏览器中的多点触控和鼠标输入” 中的解决方案。
拖动逻辑本身最初依赖于 event.targetTouches
TouchList。Internet Explorer 中不存在此成员。拖动逻辑尝试从 TouchList
中的第一个项收集 pageX
和 pageY
属性,但在 Internet Explorer 中,这些值直接在事件对象上找到。
var curX = event.targetTouches[0].pageX;
使用逻辑 OR 运算符,我指示 curX 保存 event.pageX 的值,只要 event.pageX 存在于事件对象上。如果找不到此属性,则查找 targetTouches 列表。
var curX = event.pageX || event.targetTouches[0].pageX;
如果找不到 event.pageX
,我们则回退为将 targetTouches[0].pageX
的值赋给我们的变量。
另一个需要牢记的重要事项是,此网站最初响应 touchmove
。当在触摸播放列表时引发此事件时,代码会尝试根据您的触摸移动来重新定位播放列表。触摸没有悬停 — 您要么在触摸,要么不在。
现在我们将鼠标事件链接到此逻辑,我们引入了悬停的可能性。因此,虽然 touchmove 可以自由地在播放列表上方重新定位我们的播放列表,但我们不想对 mousemove 做同样的事情。事实上,只有当鼠标按钮按下时,我们才希望 mousemove 事件重新定位播放列表。
有关如何定位所有浏览器的更多阅读和示例,请参阅 “处理所有浏览器中的多点触控和鼠标输入”。
测试两种体验
回想一下我们之前的特征检测,我们首先检查用户的浏览器是否支持 HTML5 视频。如果支持,我们提供 HTML5。如果不支持,我们提供 Flash。测试我们工作的简单方法是使用不支持 HTML5 功能的浏览器或 文档模式。使用 Internet Explorer 可以非常轻松地测试这一点。
- 按 F12 显示开发人员工具
- 将您的文档模式更改为 Internet Explorer 7 标准。
- 刷新页面。
如果我们的功能检测条件编写正确,您现在应该会看到一个基于 Flash 的演示。将您的文档模式切换回 Internet Explorer 9 标准(如果您使用的是 IE10,则为“标准”),您将回到 HTML5 体验。
动手吧!
希望这篇帖子有助于定义允许您的 iOS 网站在 IE10 Metro 和其他无插件环境中正常工作的更改类型。通过包含最佳实践,例如功能检测和负责任地使用供应商前缀来实现出色的新功能,您应该能够为用户提供出色的体验,无论他们使用何种浏览器或设备。为了协助在其他无插件环境中进行测试,请下载 Internet Explorer 10(目前仅在 Windows 8 CP 中提供),并立即开始测试!