看文章:Flash Embedding Cage Match
http://www.alistapart.com/articles/flashembedcagematch/
“How can you best embed Flash content?”
It should be a simple question, but is likely to evoke a lot of different opinions and arguments, as each of the many available embedding techniques have their own pros and cons. In this article, I will look into the complexities and subtleties of embedding Flash content and examine the most popular embedding methods to see how good they really are.
The key ingredients of a great Flash embedding method
Before we get into the nitty-gritty, let’s first define our ideal Flash embedding method. In my opinion, the following criteria matter the most:
Standards compliance
Web standards offer a universal language to a whole ecosystem of browser makers, tool builders, and web authors, so that all of these audiences can avoid compatibility problems, vendor lock-ins, and patent-infringement issues. They also enable you, the developer, to create valid HTML, which is often a project requirement.
Cross-browser support
Support for all major browsers and popular operating systems is a critical requirement. To support my research, I have created a Flash embed test suite to assess browser support for various markup-based embedding methods. The test suite includes information on parameters including support for different Flash publish settings, streaming, and scriptability.
Support for alternative content
In all cases where you like to create search-engine–friendly content or content that is accessible to people who browse the web without plugins, alternative content is your best friend.
Avoidance of Flash content/player mismatches
Unfortunately for all of us, the Flash player will attempt to play Flash content no matter what Flash plugin version that content was published for. This works fine as long as an outdated plugin doesn’t encounter functionality that is only available in higher plugin versions. If it does, your users will see broken content or no content at all. We want to avoid that.
Auto-activation of active content
As the result of the Eolas patent infringement, Microsoft updated its web browsing software so visitors can no longer directly interact with Microsoft ActiveX controls loaded by object and embed
elements, also called “active content.”
In short, Microsoft browsers will not allow any user interaction with active content until the user clicks to activate the control. To avoid litigation, Opera also introduced a similar click-to-activate mechanism. These mechanisms work like speed bumps in the road: you have to break, slowly drive over it, and push the gas pedal again. It can confuse the casual web surfer and annoy the more experienced user.
Ease of implementation
Simplicity counts. Why jump through hoops if things can be done more easily?
Flash embed basics: embed
and object
There are two HTML elements that enable you to insert Flash content into a web page. On one hand, you have the proprietary embed
element, which is supported by all major browsers (Line wraps marked » —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>
On the other hand, you have the object
element, which is a W3C recommendation. Because the W3C specifications leave quite a bit of room on how to implement plugin content, two different object implementations have emerged over time.
Most modern browsers have implemented a standards-compliant alternative to the embed
tag by using the object’s MIME-type to locate the appropriate plugin (Line wraps marked » —Ed.):
<object type="application/x-shockwave-flash" » data="myContent.swf" width="300" height="120"> <p>Alternative content</p> </object>
This method is not browser-specific and is therefore the preferred implementation.
The second implementation comes from Internet Explorer on Windows, which requires that you define the object’s classid
attribute so a browser can load the proper Flash player ActiveX control, which is a valid but browser-specific implementation (Line wraps marked » —Ed.):
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8- » 444553540000" width="300" height="120"> <param name="movie" value="myContent.swf" /> <p>Alternative content</p> </object>
Note: In the last two code examples I have deliberately omitted the codebase parameter, which is often used to point to the URL of the Flash plugin installer on Adobe’s servers, so a browser can automatically download it if it is not already installed. However this is illegal according to the specifications—which restrict its access to the domain of the current document only—and therefore not supported by all modern browsers.
Why embed
is still around
From a web standards point-of-view it may be easy to just cut the embed
element out of the equation. It simply has never been a W3C recommendation and it never will be, because of patent issues. However in reality it does have better cross-browser support than each single implementation of the object
element alone. As a result it has even become the markup of choice on popular websites like Google Video and Brightcove.
Although web standards are created to avoid compatibility issues, the embed
element is more unambiguous than the W3C-approved object
element. embed
’s strict implementation rules and good support have turned it into a de facto standard that will haunt us until we’ve had universal support for the object
element long enough to begin ignoring the browser versions that don’t like it.
So what are those browser-support problems?
Where web standards support is broken
The dual implementation of the object
element technically doesn’t break web standards, but it is the root of many problems. As a consequence, we need to find a way to combine the two object implementations into a single technique. To make matters worse, we also have to deal with browsers that have (or used to have) a broken object implementation. Let’s take a look at the problems:
- The generic object implementation breaks in Internet Explorer on Windows. IE loads the plugin and the SWF file, but doesn’t display the Flash content.
- When we partially combine the two implementations by adding the
movie
parameter to the generic implementation, Internet Explorer is able to display the Flash content, but not to stream it. - If we completely merge the two implementations, everything works fine in Internet Explorer, but Gecko-based browsers ignore the Flash content and show alternative content instead.
One of the features of the object
element is that you can nest different implementations within each other (Line wraps marked » —Ed.):
<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>
Unfortunately, a bug in older versions of Internet Explorer treats nested object
elements as if they are in series, attempting to render both of them.
To make matters worse, versions of Safari up to and including version 1.2.2 on Mac OS 10.3 ignore the object
element’s nested param
elements, though they support the same attributes for the embed
element.
Note: You may also be asking yourself how much sense it makes to define the same content, attributes, and parameters twice, as in the above technique. This combined method also makes it harder to, for example, use JavaScript to communicate with your Flash content, as you would have to test which object you communicate with.
We are slowly moving towards a better world: some of these bugs are already fixed, but Internet Explorer’s non-generic and buggy object
implementation is still stifling the adoption of the object
element. We can only hope that this will be fixed in the near future.
Why object
is better than embed
Despite its current lack of cross-browser support the object
element still has more to offer than the embed
element, because in addition to being standards-compliant, it also offers superior support for alternative content.
The object
element allows you to nest alternative content inside of it, and this content is displayed if an implementation is not supported or a plugin is not installed. This content will also be picked up by search engines, making it a great tool for creating search-engine–friendly content.
The embed
element supports alternative content by offering the noembed
element, but this only works for those few browsers that don’t support the embed
element itself, like Internet Explorer on Windows Mobile platforms. Unlike the object
element, embed
doesn’t support alternative content when the embed
element is supported, but the Flash plugin is not installed. In that situation, it relies on the pluginurl
and pluginspage
attributes to display an image broken puzzle piece that can be clicked on to install the plugin. Very 1990s.
I believe it’s a much better solution to display alternative content that describes the plugin content and includes a subtle indication that a user can have a richer experience by downloading the Flash plugin. (This is yet another reason we don’t really need the often-misused codebase
attribute.)
To summarize, the object
element offers significant advantages over the embed
element if you prefer to create standards-compliant code or accessible, search-engine–friendly content.
The limitations of markup-only methods
Considering the criteria we defined earlier—standards compliance, cross-browser support, support for alternative content, avoidance of player/content mismatches, auto-activation of active content, and ease of implementation—it’s easy to see that there’s really only so much that markup can do by itself.
Although markup offers the means to display Flash content or alternative content, it will not be able to provide us with a solution to avoid Flash content and plugin mismatches or a workaround to avoid having to click to activate active content, and it’s not always the simplest thing to implement, either.
Still, let’s take a quick look at the most popular markup “combination” methods.
The twice-cooked method
In the Flash IDE, you can publish HTML pages that include the so-called twice-cooked method, combining Microsoft’s specific object implementation with the proprietary embed
element nested inside it as alternative content (Line wraps marked » —Ed.):
<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>
As you can see, this method is solely based on proprietary and vendor-specific markup. It clearly focuses on cross-browser support only, and isn’t standards compliant.
The twice-cooked method is redundant, makes your web pages invalid, and doesn’t include a mechanism for inserting alternative content. And the only reason it’s easy to use is because the Flash IDE generates it: don’t ask anyone to reproduce it from memory.
The nested-objects method
Nesting the two object implementations is a good alternative to the twice-cooked method, because it add standards compliance and supports alternative content. Let’s take a look (Line wraps marked » —Ed.):
<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>
Unfortunately it still lacks cross-browser support due to Internet Explorer’s nested object
element bug and Safari’s lack of support for nested param
elements. A common workaround uses proprietary Internet Explorer conditional comments to avoid this browser’s pitfalls (Line wraps marked » —Ed.):
<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>
Besides the previously mentioned redundancy, this method is even more complex to author and it uses an ugly workaround that may complicate the process of generating code using server-side languages.
Flash Satay
Another option is the Flash Satay method, which is based on the generic object implementation and includes an additional movie
parameter to avoid the bug that causes Flash content not to display in Internet Explorer. It also includes a container Flash movie to fix the Internet Explorer streaming bug (Line wraps marked » —Ed.):
<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>
Although it approaches the “ideal” generic object implementation, Flash Satay contains a workaround whose architectural implications won’t suit everybody—and it also doesn’t support nested param
elements in older versions of Safari.
The case for DOM scripting
When you consider the limitations of markup-only methods, you might wonder why we shouldn’t just use a DOM script instead. With scripting, we could dynamically serve the markup each browser requires:
- The browser-specific object implementation for Internet Explorer.
- The proprietary
embed
element for older versions of Safari. - The generic object implementation for all other browsers.
A DOM script is also flexible enough to solve our other problems: for starters, we can use it to solve the Flash player and content/player mismatch problem by detecting the plugin version and testing per Flash movie whether Flash content or alternative content needs to be shown. When the required plugin version is not available we could trigger Adobe express install, a mechanism built into the Flash player that simplifies the plugin-download process.
A DOM solution also allows us to avoid click-to-activate mechanisms by dynamically writing the object
elements.
Things to keep in mind when using JavaScript
Because not everybody is an experienced JavaScripter—and because even those who are shouldn’t have to reinvent the wheel—it makes sense to use an existing Flash-embedding JavaScript library. Let’s consider the criteria for selecting a trustworthy library.
Standards compliant markup is rarely promoted by library-makers, because these libraries either define your Flash content in JavaScript itself or in a development tool that does the work for you. Most libraries generate invalid HTML and, because the markup is written dynamically, the W3C validator is unable to check your markup.
Another point to consider is how well a method degrades when JavaScript is disabled, unsupported, or not supported well enough. What would be the point in using JavaScript to improve the user experience for one group of visitors, when by doing so you break it for another?
With all of this in mind, let’s take a look at some of the most popular libraries to see how well they behave.
The Adobe Flash player detection kit
Besides the generated markup in the Flash IDE Adobe also provides the Flash player detection kit. There are three ways of using this kit:
- Checking the Detect Flash Version checkbox (under File > Publish Settings > HTML) in the Flash 8 IDE.
- Inserting it manually after downloading the detection-kit ZIP file.
- Publishing from Flex Builder 2, where it is included by default.
Although the detection kit contains all the features we want, like version detection, express installation, and auto-activation of active content, it has room for improvement. When it comes to standards compliance it hopelessly fails: it is still based on the twice-cooked markup, so it either generates Microsoft’s vendor-specific object implementation or the proprietary and noncompliant embed
element.
It also supports alternative content although in an odd and inconsistent manner. You need to define your alternative content twice: in both inline JavaScript and in a noscript element
. And while the Flash IDE and the downloadable detection kit leave it up to you in terms of what to define as alternative content, Flex 2 by default inserts the whole twice-cooked code blurb in the noscript element in order to display Flash content in web browsers that either don’t support JavaScript or have JavaScript disabled.
All in all, the detection kit lacks a good strategy and a set of documented best practices on how to either promote proper degradation or make Flash content search-engine–friendly and accessible. And it’s still not the easiest solution to use if you need to customize it.
UFO and SWFObject
Popular open source alternatives like my own UFO and Geoff Stearns’s SWFObject are probably the most complete and easiest-to-use libraries currently available.
Although they look similar on the surface, the two libraries are quite different under the hood. For example, SWFObject uses Adobe’s twice-cooked method, while UFO generates mostly standards-compliant markup. On the other hand they do share architectural principles: both libraries are built around the idea that you author marked-up alternative content (thus creating accessible, search-engine–friendly content) that is replaced by a DOM script when the required Flash and JavaScript support is available.
Despite their explicit support for alternative content, both libraries still have one fundamental weakness: they rely on JavaScript to insert Flash content. As a result a small group of visitors that have the Flash plugin installed but have JavaScript disabled or lack sufficient JavaScript support will see only alternative content.
The case for unobtrusive DOM scripting
To solve the degradation issue, it would make more sense to use a truly unobtrusive embedding method: a hybrid solution that uses standards compliant markup like that used in the Flash Satay method to embed both Flash content and alternative content while employing an unobtrusive DOM script that serves as an add-on to fix problems and add services to modern browsers.
ObjectSwap is based on these principles and is in my opinion an architectural role model for future Flash embedding libraries. Unfortunately, ObjectSwap focuses primarily on the auto-activation of active content, so it isn’t as useful for version detection and markup-support problems like the lack of streaming support in Internet Explorer or parameter support in older versions of Safari.
Another point it can be improved on—which is something to keep in mind for all unobtrusive libraries—is performance. By using the onload
event, your DOM related behavior is only applied after a whole page, including all of its assets, is loaded. A better alternative would be the DOMContentLoaded
event, which lets you apply your behavior as soon as a page’s DOM is available. Because the DOMContentLoaded
event is not fully supported across browsers yet, you could use this solution instead.
The future of Flash embed
Although the perfect solution may not have been written yet, I do think we are already pretty far into the right direction. If we combine the best features of different libraries, we should be able to fulfill all the criteria we defined at the beginning of this article.
In search of the solution, Geoff Stearns and I have teamed up. Instead of rewriting UFO and SWFObject, we are working on a single new, standards compliant and completely unobtrusive solution that will eventually replace both libraries. The fruit of this co-operation is the SWFFix project, which is currently in the alpha phase.
Step up, software makers!
Although web developers can come up with creative workarounds, it is the browser and plugin makers who have the ability to make real progress. By fixing outstanding bugs and adopting embedding solutions that support both web standards and promote the use of alternative content, they could give developers the means to do their daily jobs the right way. So how about it, software developers?