A List Apart's Flash Satay method improved

Standard compliant Flash movies that don’t fail in Internet Explorer.

In its issue number 154, A List Apart contributor Drew McLellan published an article entitled Flash Satay which allowed web authors to embed flash movies in XHTML pages while complying to the the W3C’s standards. Unfortunately this method failed on some versions of Internet Explorer, whose users represent a non-negligible part of the web audience. The method that follows corrects this problem and truly allows flash and standards to coexist.

The original code from A List Apart

Here is the original code as proposed by Drew, we are going to use the same code for our method.

<object
  type="application/x-shockwave-flash"
  data="c.swf?path=movie.swf" 
  width="400"
  height="300"
>
<param
  name="movie"
  value="c.swf?path=movie.swf"
/>
<img
  src="noflash.gif"
  width="200"
  height="100"
  alt=""
/>
</object>

The problem

Through the discussion that followed Drew McLellan’s article, it was realised that some versions of Internet Explorer 5.5 had corrupted Flash players installed and displayed a text box instead of a Flash movie. Simply upgrading the Flash player will not do: the only way to solve this problem would be to uninstall and then reinstall the Flash player, something which the web audience will not do when asked—understandably so since its Flash player does work in every site but this one. Various methods were tried, including the Microsoft proprietary conditional comments, but failed to provide all of four things:

Possible solutions

There would be two basic methods for solving the problem: either do a server-side rewrite based on the user-agent string, or do a client-side rewrite. While the first method is seamless to the user, it requires server-side processing which is not always possible depending on the host, and it requires that all web pages be parsed by the server before they can sent to the user, which slows the response time. The second method requires client-side scripting to be enabled (which usually is the case), but there might be a short moment when the user can see the text box before it gets replaced by the proper Flash movie. The second method is the one that will be used here.

JavaScript to the rescue

Through the various tests that were done, it turned out that putting back the classid attribute allowed Internet Explorer to play the Flash movie correctly, but this alternative prevented Netscape from playing the said movie. It would seem rather easy, through some simple unobstructive javaScript and the DOM, to tell Internet Explorer to loop through all the object tags and simply add the darned classid attribute if the object contains a Flash movie, which is exactly what we try to do in the following example:

function flashFix(){
  if(navigator.appVersion.indexOf("MSIE 5.5")!=-1){
    for(i=0;a=document.getElementsByTagName("object")[i];i++){
      if(a.getAttribute("type") &&
         a.getAttribute("type").indexOf("application/x-shockwave-flash")!=-1 &&
         !a.getAttribute("classid")
         ){
            a.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
          }
    }
  }
}

In theory this would work perfectly, but it doesn’t: Internet Explorer won’t let us change the classid of an element once it has been rendered on screen. An other method is needed.

Out with it!

A new idea comes to mind: why not substitute the old object tag for an object tag with exactly the same content with an added classid? The DOM’s outerHTML and the slice() method for strings would be perfect to do that:

function flashFix(){
  if(navigator.appVersion.indexOf("MSIE 5.5")!=-1){
    for(i=0;a=document.getElementsByTagName("object")[i];i++){
      if(a.getAttribute("type") &&
         a.getAttribute("type").indexOf("application/x-shockwave-flash")!=-1 &&
         !a.getAttribute("classid")
         ){
            a.outerHTML=a.outerHTML.slice(0,a.outerHTML.indexOf(">"))+
            " classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' "+
            a.outerHTML.slice(a.outerHTML.indexOf(">"),a.outerHTML.length);
          }
    }
  }
}

That should work perfectly, but it still doesn’t. Writing alert(a.outerHTML); shows us what the problem is: the param tag doesn’t appear! As seen in Drew’s article, the param tag is what tells Internet Explorer what movie to load, without it Internet Explorer sits there and displays a blank movie.

The final fix

Knowing how close we are to our goal, we set out to write the final code which will solve Internet Explorer’s woes: we need to add the param tag and give it the right value for movie, which we can do since we already have a data attribute in our object tag which contains the location of our Flash movie.

function flashFix(){
  if(navigator.appVersion.indexOf("MSIE 5.5")!=-1){
    for(i=0;a=document.getElementsByTagName("object")[i];i++){
      if(a.getAttribute("type") &&
         a.getAttribute("type").indexOf("application/x-shockwave-flash")!=-1 &&
         !a.getAttribute("classid")
         ){
            a.outerHTML=a.outerHTML.slice(0,a.outerHTML.indexOf(">"))+
            " classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' "+
            "><param name='movie' value='"+
            a.getAttribute("data")+
            "' \/>"+
            a.outerHTML.slice(a.outerHTML.indexOf(">")+1,a.outerHTML.length);
          }
    }
  }
}

Now it works perfectly. All that’s left to do is to call the function that we created when the page loads:

window.onload=function(){
  flashFix();
};

And then put it all in a .js file and link to it in our HTML document’s head with:

<script
  type="text/javascript"
  src="flash_fix.js"
>
</script>

A compressed version

The javaScript file still is 637 bytes, and we feel that we could squeeze it just a little more. The following file is a compressed version of only 495 bytes. Now that’s unobstructive javaScript!

Plaintext source of this program, released under the GPL.