“怎样才是最佳的flash嵌入方式呢?”
本来应该是一个简单的问题,但是应该会引起很多不同的看法和争论,因为可用的嵌入技术很多,而每种都有其支持和反对者。这篇文章里,我将详述嵌入flash的复杂性和技巧性,并且对最流行的嵌入方法加以考察,看看它们到底好不好。
好的flash嵌入方法的关键
在进入本质性讨论之前,让我们先定义一下理想的flash嵌入方法。在我看来,下面这些因素是最重要的:
遵循标准
web标准为浏览器厂商、工具软件程序员以及网页作者提供了一种易于理解的通用语言,使得所有的用户都得以避免兼容性、垄断和专利侵权的问题。他们也让你——开发者——能够创建出项目所需的正确的网页。
跨浏览器支持
支持所有主流浏览器和常见操作系统是很关键的需求。为了支持我进行的研究,我创建了一个flash嵌入测试套件来评估浏览器对于各种嵌入方法的支持。该套件对各种不同的flash发布设置、流加载和脚本类型作了相应测试。
对替换内容的支持
无论何时你想要创建对搜索引擎和对未安装插件的浏览者友好的网页时,替换内容都是你最好的朋友。
避免flash和播放器的不匹配
不幸的是,对所有人来说,flash播放器总是试图去播放flash内容,不管flash播放器的版本和flash内容发布时的版本是什么。如 果使用老版本播放器的flash没有用到新版本播放器才支持的功能的话还好,否则你的用户将会看到不完整的内容或者完全就没有内容。
对活动内容的自动激活[1]
由于Eolas专利侵权问题,微软更新了它的浏览器,以至于浏览者再也不能直接与“活动内容”,也就是用object和embed标签嵌入的ActiveX控件进行交互。
简而言之,微软浏览器要求用户先点击才能跟活动内容交互。为了避免被起诉,Opera也引入了类似的点击激活机制。这些机制就像公路上的减速带,你需要刹个车,慢慢开过去。它会让一般用户搞不清状况,让有经验的用户感到恼火。
容易实现
简单就是胜利。轻松就能做到的事情为什么要去费尽力气?
Flash嵌入基础:embed和object
有两个标签可以让你在网页上嵌入flash。一个是已有的embed,所有的浏览器都支持它。(» 表示连接换行 -Ed)
<embed type="application/x-shockwave-flash" »
src="myContent.swf" width="300" height="120" »
pluginspage="http://www.adobe.com/go/getflashplayer" />
<noembed>Alternative content</noembed>
另一个是W3C建议的object. 因为W3C规范给如何实现插件内容留了相当大的空间,结果出现了两种不同的object实现方式。
大多数现代浏览器实现了遵循标准的方式,将embed标签替换为object的MIME类型,以此来确定插件的类型。
<object type="application/x-shockwave-flash" »
data="myContent.swf" width="300" height="120">
<p>Alternative content</p>
</object>
这种方式是各种浏览器通用的,所以是比较好的实现。
第二种实现来自Windows的IE,其中需要定义object的classid属性,这样浏览器才能正确载入Flash播放器的ActiveX控件。这种实现是有效的,但不是浏览器通用的。
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8- »
444553540000" width="300" height="120">
<param name="movie" value="myContent.swf" />
<p>Alternative content</p>
</object>
注意:这两个例子中我都故意省略了codebase参数,这个参数通常指向Adobe的Flash插件安装包位置,用来在浏览器没有安装插件的时候自动下载。但这是和标准中禁止文档访问跨域资源的精神相违背的,而且有些现代浏览器并不支持。
为什么还有embed
从web标准的观点来看,可能会觉得直接砍掉embed标签就可以了,但这从来没有也永远不会成为一个W3C建议,因为专利问题。但是实际上它 确实比单独的object标签拥有更好的跨浏览器支持。所以它现在成了许多流行的站点诸如Google Video和Brightcove的选择。
尽管web标准是为了避免兼容性问题而建立的,但embed却比W3C支持的object更为明确。embed的严格实现规范和良好支持已经把 它变成了一项事实上的标准,它会一直阴魂不散,直到object标签得到广泛并且足够长时间支持,长到已经可以忽略那些不喜欢它的浏览器版本。
那么现在所谓的浏览器支持性的问题到底是什么?
web标准不被支持的地方
object的两种实现并没有摧毁web标准,但它是很多问题的根源。后果之一是,我们需要找一个方法去把两种object实现联合在一起构成一个统一的技术。更糟糕的是,我们不得不处理那些(曾经)对object不完全支持的浏览器。来看下这些问题:
* 一般的object实现在Windows IE下是不行的,IE加载了插件和swf文件,但却根本不显示flash内容。
* 我们加入movie参数,以部分地联合这两种方案的时候,IE可以显示flash内容了,但却不进行流加载。
* 当我们完全把两种方案联合在一起的时候,IE正常了,但是Gecko核心的浏览器忽略了Flash内容并且显示替换内容。
object的一个特性是,你可以把不同的实现方案嵌套放在一起:
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8- »
444553540000" width="300" height="120">
<param name="movie" value="myContent.swf" />
<object type="application/x-shockwave-flash" »
data="myContent.swf" width="300" height="120">
<p>Alternative content</p>
</object>
</object>
不幸的是,老版本的IE在处理嵌套的object的时候存在bug:试图去渲染两个flash。
更糟的是,Mac OS 10.3上,版本在1.2.2以下(含1.2.2)的Safari会忽略object里面嵌套的param标签,虽然他们支持embed标签的同样属性。
注意:你可能会问你自己,上面的方法里把同样的内容、参数、属性写两遍到底有多大意义。比如说,这种联合方法让JavaScript跟Flash内容通信更困难,因为你要先测试你到底在跟哪个object通信。
我们朝着一个更好的世界缓慢的进步:这些bug中有的正在被逐渐解决,但是bug到无以复加的IE的object实现仍然使得object的采用困难重重。我们只能将解决的希望寄予在不久的将来了。
为什么object比embed更好
尽管object缺乏跨浏览器的支持,但是object仍然比embed提供了更多的东西,为了跟进标准,它还提供了对于替换内容的良好支持。
object标签允许你在它里面包含替换内容,如果这种实现不被支持,或者插件没有安装,那么替换内容将被显示。这个内容还将被搜索引擎收录,使得它成为创建对搜索引擎友好内容的好帮手。
embed标签支持noebmed标签所提供的替换内容,但是它只被那些连embed标签都不支持的浏览器支持,例如Windows Mobile上的IE。不像object,当embed被支持但是却没有安装flash插件的时候,它并不支持替换内容。它依赖于pluginurl和 pluginspage属性来显示一个谜一样的破烂图片,点一下可以去安装插件。非常的90年代。
我相信提供一个关于插件内容的替换内容,并且同时给出巧妙的提示,让用户知道下载flash插件会获得更好的体验,这样的解决方案是更好的。(还有一个原因就是我们不是那么需要这个经常被用错的codebase属性)
纯标签方法的局限性
考虑一下我们之前定义的原则——顺应标准,跨浏览器支持,支持替换内容,避免播放器与内容不匹配,自动激活活动内容,和便于实现——显而易见的是仅靠标签能做到的太有限了。
虽然标签表示了flash内容或替换内容的意义,但它没法给我们提供一种解决方案来避免flash内容和播放器不匹配或者一种变通方法来避免点击激活,而且它也并不总是最简单的实现方法。
不过,还是简单的看一下现在最流行的标签“联合”方法吧。
两次烹饪法
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8- »
444553540000" codebase="http://fpdownload.macromedia.com »
/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" »
width="300" height="120">
<param name="movie" value="myContent.swf" />
<embed type="application/x-shockwave-flash" »
src="myContent.swf" width="300" height="120" »
pluginspage="http://www.macromedia.com/go »
/getflashplayer" />
</object>
如你所见,这个办法仅仅把通用的和专用的标签弄在一起,显然是只为了解决跨浏览器支持问题,并不和标准兼容。
两次烹饪法显得冗余,并且使你的网页不标准,还没有提供一种显示替换内容的机制。它易用的唯一原因是Flash IDE生成了它:所以,别再让任何人就凭着记忆像这样写了。
嵌套object法
嵌套object法是两次烹饪法的一个不错的替代品,因为它遵循标准并且支持替换内容。我们看下:
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8- »
444553540000" width="300" height="120">
<param name="movie" value="myContent.swf" />
<object type="application/x-shockwave-flash" »
data="myContent.swf" width="300" height="120">
<p>Alternative content</p>
</object>
</object>
不幸的是,它支持仍然缺乏跨浏览器的支持,对于IE的嵌套object bug和Safari的嵌套param bug仍然无能为力。一个常用的方法是利用IE的条件注释来避免这个浏览器的陷阱。
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-»
444553540000" width="300" height="120">
<param name="movie" value="myContent.swf" />
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" »
data="myContent.swf" width="300" height="120">
<!--<![endif]-->
<p>Alternative content</p>
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>
除了前面提到的冗余问题,这个方法还更麻烦,因为它使用了一个丑陋的权宜方式,会使得用来生成页面的服务器端代码更加复杂。
Flash Satay
另外还有个选择是Flash Satay方法,它用了传统的object方式,并且增加了额外的movie param来避免IE中不显示的bug。它还包括了一个容器flash来处理IE流加载bug。
<object type="application/x-shockwave-flash" »
data="c.swf? path=myContent.swf" width="300" height="120">
<param name="movie" value="c.swf?path= »
myContent.swf" />
<p>Alternative content</p>
</object>
虽然它很接近“理想的”object实现,但Flash Satay包含了一个使它的结构无法普适的权宜方案,并且它还不支持老Safari中的嵌套param。
DOM脚本的方案
在考虑纯标签方式的时候,也许你已经在想,为什么我们不用DOM脚本来替代呢。通过脚本,我们可以为每个浏览器提供所需的标签:
* IE专用的object标签
* 老版本的safari用的embed标签
* 所有其他的浏览器用的普通object
DOM 脚本也具有足够灵活性来解决我们的其他问题:初始化的时候,我们可以用它来解决flash内容和播放器不匹配的问题。只需要对每个flash逐一检测插件 版本,并决定显示Flash还是显示替换内容。当需要的插件版本不足的时候,我们可以触发Adobe快速安装,一种可以在flash播放器内部下载安装控 件的简单机制。
DOM方案也让我们以动态写入的方式避免了点击激活机制。
用Javascript的时候要考虑的事情
因为不是每个人都是javascript专家——即使是专家,也没有必要重新去发明轮子——去用已有的javascript库嵌入flash是很明智的。让我们考虑一下选择一个值得信任的库所依据的原则。
写库的人通常都不考虑遵循标准,因为这些库要么在javascript里面生成flash内容,要么在开发工具里面帮你做这个工作,大多数库生产的都是不标准的代码,而且由于是动态生成的,W3C验证器也没法检查你的标签。
另外要考虑的是,当javascript被禁用,不支持,或者不被完全支持的时候,能否做到良好的退化。为了帮一小部分人提升用户体验而采用javascript,结果却弄得另一小部分人没法体验了,这样做的意义在哪里?
带着这些问题,我们来看看现在流行的库能做到多好。
Adobe Flash player detection kit
除了在flash IDE里面生成标签代码以外,Adobe还提供了Flash播放器侦测套件。使用这个套件有三种方法:
1. 在flash8 IDE中勾上检测flash版本的多选框(在文件>发布设置>HTML)。
2. 下载侦测套件zip,然后手动加入。
3. 用Flex Builder2发布,默认就带。
虽然侦测套件包含了我们想要的所有特性,像版本检测,快速安装,自动激活,但它仍然有改进的空间。如果考虑遵循标准的话,它就显然失败了:它仍 然基于两次烹调法,所以它要么生成微软的独门object,要么生成通用的不标准embed。它也支持替换内容但是以一种古怪的不统一的方式。你需要两次定义你的替换内容:在javascript里和在一个noscript标签里。然而一方面Flash IDE和侦测套件把设置替换内容的工作流给你,另一方面Flex Builder 2却默认会把整个两次烹调代码贴在noscript标签里面,用以在不支持javascript或者禁用了javascript的时候显示flash。
总的来说,侦测套件在如何选择正确退化或者使flash内容对搜索引擎友好可访问这一点上,缺乏一种良好的策略和可以自圆其说的最佳方法。而且如果你需要自定义的话,它也并不是最简单的解决方法。
UFO和SWFObject
流行的开源替代品,例如我的UFO和Geoff Stearns的SWFObject,则可能是现在最完整和易用的库了。
虽然他们表面上看起来很像,但是两个库在底层是非常不一样的。比如说,SWFObject使用Adobe的两次烹调法,而UFO则生成了最标准的标 签。另一方面,他们都遵循了相同的构架原则:当所需的flash和javascript存在时,使用DOM脚本重写网页上的替换内容。
虽然他们都有对替换内容的支持,但是两个库都有一个共同的弱点,他们依赖javascript去插入flash,所以一小撮只有flash插件而没有javascript支持的用户就只能看见替换内容。
低调的DOM脚本
为了解决退化问题,使用一种低调脚本的嵌入方法会比较有效:这种混合方法使用Flash Satay那样的标准标签来嵌入Flash内容和替换内容,同时并不太显眼地调用DOM脚本,作为一种扩展来为浏览器解决问题和增加功能。
ObjectSwap就是基于这样的原则,并且我认为将会是未来flash嵌入库的榜样架构。不幸的是,ObjectSwap注意力主要集中在解决自动激活上,所以在解决版本检测和诸如IE上的流bug和老Safari的param bug这样的标签支持问题上,就没有多大作用。另一个可以提高的地方在于——所有低调库都应该加以注意的是——性能。使用onload事件的话,你的 DOM相关代码只会在你整个页面,包括页面上的元素都加载完成后才执行。用DOMContentLoade事件来代替会更好,那就会在DOM加载完后立即触发。但是DOMContentLoade并没有被所有浏览器支持,你也许可以用这个方案代替。
flash嵌入的未来
虽然完美的方案也许还没有被写出来,但我确实觉得我们已经在正确的方向上走了很远。如果我们把不同库的好特性组合在一起,我们应该能够实现我们在文章开头定义的那些原则。为了研究这样的解决方案,Geoff Stearns和我已经共同工作,我们正在努力写一个新的标准的并且低调的解决方案,而不是重写UFO和SWFObject。它会最终替代这两个库。合作的成果是 SWFFix项目,现在是alpha阶段。[2]
动作快点,软件老大们!
虽然web开发者可以想出创新的变通方案来,但是只有浏览器和插件厂商可以做出真正实质性的进步。改掉那些明显的bug,采用支持web标准和替换内容的嵌入方法,他们可以让开发者得以用正确的方式来进行日常工作。所以软件老大们,觉得如何?
by Kevin Cornell
译注:
[1]微软新的补丁已经在浏览器中取消了这个特性。
[2]UFO和swfobject1.5的升级替代品目前是swfobject2.0,实际使用的html是上面讲的条件注释方式。