Summary

This article investigates methods of getting nested objects to work correctly with Internet Explorer 6, and techniques to get around the problem of IIS not allowing objects to be nested.

Author: Gez Lemon

Contents

Embedding Objects

The embed element was introduced by Netscape, but never made it into the HTML specification. Instead, the object element was chosen as the method to embed objects into a web document. Despite never making it into the HTML specification, the embed element became the de-facto standard for ensuring cross-browser compatibility. The technique involves placing an embed element inside an object element, but as the embed element isn't part of the HTML specification, this technique doesn't validate.

In his excellent article on A List Apart, Drew McLellan devised a technique that allows a Flash movie to be embedded in a document without using the embed element. Using Drew's technique, Internet Explorer for Windows doesn't stream the movie. With small movies, this isn't a problem, but not acceptable with larger movies. For large movies, Drew gets around the problem by making a small movie which loads the real movie in the first frame, which he dubs the satay method.

Object Types

The type attribute of the object element allows you to specify the MIME type of the embedded object. This should be enough information for a user agent to determine if and how it will handle the object. For Flash, and some other objects, this technique works well. For other media types, Internet Explorer favours the classid attribute with the GUID of the appropriate ActiveX component. For example, the following technique to embed a QuickTime movie in a document works in Opera and Gecko based browsers, but won't work with Internet Explorer.

<object id="QT"
        data="magpie2_demo.qt.smil"
        type="video/quicktime"
        width="320"
        height="270">
   <param name="autoplay" value="-1"/>
   <param name="controller" value="-1"/>
</object>

For this to work in Internet Explorer, the classid attribute must be used to specify the ActiveX component's GUID.

<object id="QT"
        classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320"
        height="270">
    <param name="src" value="magpie2_demo.qt.smil"/>
    <param name="autoplay" value="true"/>
    <param name="controller" value="true"/>
</object>

Conditional Comments

Embedding one object in another results in Internet Explorer rendering the player for both objects, but only playing the movie for the object with a classid attribute. To work around this, Roberto Scano, originally working on a Flash example, devised a technique using Internet Explorer's conditional comments that would also work with other objects. Internet Explorer's conditional comments were introduced with IE 5, and the syntax suggested by Microsoft for conditional comments is as follows:

<!--[if IE]>
<p>
Internet Explorer only content here.
</p>
<![endif]-->

Other browsers will treat the <!-- combination as the start of a comment, which terminates with --> just after the endif statement, leaving only Internet explorer to parse the if statement. Microsoft also suggest a syntax to exclude Internet Explorer:

<![if !IE]>
<p>
Content for everyone except Internet Explorer
</p>
<![endif]>

This example is deliberately uncommented so that other browsers render the content, but Internet Explorer respects the if !IE statement. Unfortunately, this won't validate as the content isn't well-formed. To get around this, authors add the starting comment to the conditional comments and close the comment on the same line. Using this technique reveals the content to all browsers, but Internet Explorer still respects the conditional statement.

<!--[if !IE]> <-->
<p>
Content for everyone except Internet Explorer
</p>
<!--> <![endif]-->

Nesting Objects

Using conditional comments, Roberto Scano came up with a cross-browser technique to embed QuickTime movies into a document.

<object id="IEQT" 
        classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320"
        height="270">
    <param name="src" value="magpie2_demo.qt.smil"/>
    <param name="autoplay" value="true"/>
    <param name="controller" value="true"/>

  <!--[if !IE]> <-->

    <object id="NonIEQT"
            data="magpie2_demo.qt.smil"
            type="video/quicktime"
            width="320"
            height="270">
       <param name="autoplay" value="-1"/>
       <param name="controller" value="-1"/>
    </object>

  <!--> <![endif]-->

</object>

This technique also works for other media types, including a method of embedding Flash content that doesn't require a small movie to launch the main movie.

IIS and Nested Objects

That could be the end of the story, except there is a severe problem with the way that IIS handles nested objects. Nesting an object in an ASP document results in the following error message:

Active Server Pages error 'ASP 0139'

Nested Object

/junk.asp, line 104

An object tag cannot be placed inside another object tag.

For developers using ASP, Roberto came up with a solution that uses the ASP BrowserCap component. Roberto dubs the method, Scano's Hack. Firstly, developers must ensure their browsercap.ini file includes Internet Explorer 6. The method then involves using the BrowserCap component to select the correct method of embedding the object on the server.

<%
Dim objBC

Set objBC = Server.CreateObject("MSWC.BrowserType")
%>
<object id="QT" 
<% If objBC.Browser = "IE" Then %>
        classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
<% Else %>
        data="magpie2_demo.qt.smil"
        type="video/quicktime"
<% End If %>
        width="320"
        height="270">
    <param name="autoplay" value="-1"/>
    <param name="controller" value="-1"/>
</object>
<%
' Tidy up
Set objBC = Nothing
%>

Server-side hacks are always preferable over client-side hacks, but this technique does require that developers have access to the browsercap.ini file on the server. Most hosts would be happy to ensure the browsercap.ini file is up to date, but for developers with unfriendly hosts, Internet Explorer's conditional comments could be used instead.

<!--[if IE]>
<object id="IEQT" 
        classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320"
        height="270">
    <param name="src" value="magpie2_demo.qt.smil"/>
    <param name="autoplay" value="true"/>
    <param name="controller" value="true"/>
</object>
<![endif]-->
<!--[if !IE]> <-->
<object id="NonIEQT"
        data="magpie2_demo.qt.smil"
        type="video/quicktime"
        width="320"
        height="270">
   <param name="autoplay" value="-1"/>
   <param name="controller" value="-1"/>
</object>
<!--> <![endif]-->

Using Include Files

Both of these techniques work well, but what about nested objects for graceful degradation? The W3C suggest nesting object elements for graceful degradation. The example they provide starts with a Java applet. If that fails, an MPEG movie is displayed. If the MPEG movie fails, a GIF image is displayed. If the GIF image fails, text is displayed in its place.

<object classid="java:Press.class" width="500" height="500">
    <object data="Pressure.mpeg" type="video/mpeg">
        <object data="Pressure.gif" type="image/gif">
            As temperature increases, the molecules in the balloon...
        </object>
    </object>
</object>

Discussing this issue with Roberto, we couldn't see how the BrowserCap component or Internet Explorer's conditional comments would cater for this. Experimenting with different ideas, we discovered an interesting side effect of using include files. It appears that IIS evaluates nested objects before include files are processed, effectively hiding it from IIS. Going back to Roberto's original idea of using Internet Explorer's conditional comments, we found that the following worked fine in an ASP document, where the nonieflash.asp document contains the nested object.

<object id="IEQT"
        classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320"
        height="270">
    <param name="src" value="magpie2_demo.qt.smil"/>
    <param name="autoplay" value="true"/>
    <param name="controller" value="true"/>
<!--[if !IE]> <-->
<!--#include file="noniecontent.asp" -->
<!--[endif]-->
</object>

Getting IIS to Gracefully Degrade

Experimenting further, we discovered that the same principles could be used for the graceful degradation example provided by the W3C. Each nested object requires its own include file, that contains another include file if necessary. Unfortunately, Internet Explorer still tries to render all objects as it doesn't follow the standards for graceful degradation.

<object classid="java:Press.class" width="500" height="500">
<!--#include file="nonjava.asp" -->
</object>

The file nonjava.asp contains the object element required to display the MPEG movie.

<object data="Pressure.mpeg" type="video/mpeg">
<!--#include file="nonmpegcontent.asp" -->
</object>

And finally, the file nonmpegcontent.asp contains the object element required to display an image. No further objects are nested at this point, so a text explanation is provided within the object element as normal.

<object data="Pressure.gif" type="image/gif">
    As temperature increases, the molecules in the balloon...
</object>

Conclusion

The technique is clumsy, but allows developers to work around IIS's non-standard behaviour regarding nested objects. Internet Explorer gets it wrong and tries to render all of the objects, but we're rapidly approaching an era where Internet Explorer's behaviour is no longer acceptable. Internet Explorer is today what Netscape 4 was three years ago. For more information on embedding objects in a document, see Joe Clark's Standards-compliant Web pages with captioning.

Update - The Dynamic Object Approach

In comment 4, Danny Tuppeny (a gifted colleague of mine) discovered that you can also throw IIS by creating the object tags dynamically, as IIS won't evaluate dynamic content; in keeping with the server-side include hack. But more significantly, you can use dummy dynamic content to throw IIS by including empty ASP code delimiters within the object tag, like so:

    <<%%>object ...

This makes the graceful degradation example a lot less clumsy, as it doesn't require the markup to be broken out into a set of include files. Instead, each nested object just requires the empty ASP code delimiters.

<object classid="java:Press.class" width="500" height="500">
    <<%%>object data="Pressure.mpeg" type="video/mpeg">
        <<%%>object data="Pressure.gif" type="image/gif">
            As temperature increases, the molecules in the balloon...
        </object>
    </object>
</object>

That still leaves the issue of Internet Explorer's incorrect behaviour of trying to render each of the objects, but Danny's solution is a significant jump forwards. Good work, Danny.

Category: Web Standards.

Comments

  1. [object-paranoia.php#comment1]

    For Flash, and some other objects, this technique works well.

    It would be nice to have a list of media types that work without clsid in IE.

    Posted by Fox on

  2. [object-paranoia.php#comment2]

    It would be nice to have a list of media types that work without clsid in IE.

    I'm not aware of anyone maintaining a list of media types that work in IE without a classid. The obvious media types that spring to mind are image/gif and image/jpeg, but even these are buggy in IE; IE renders scrollbars around the images, and won't display the image at all if a visitor's security settings disallow ActiveX components. Given Internet Explorer's poor support of the object element, someone may well be maintaining a list of mime types that work with IE. If anyone knows of such a list, please post a link here *smile*

    Posted by Gez on

  3. [object-paranoia.php#comment3]

    For Flash, and some other objects, this technique works well

    is inconsistent with

    The obvious media types that spring to mind are image/gif and image/jpeg, but even these are buggy in IE

    Posted by Nigel on

  4. [object-paranoia.php#comment4]

    It seems IIS only throws errors for nested object tags if they're coded into the source, and not if they're output dynamically. If you replaced the word object with an ASP Response.Write, like <%="object"> in the middle of the tag, it parses fine. You could even use empty ASP tags in there somewhere, like this:

    
    <object id="IEQT" 
            classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
            width="320"
            height="270">
        <param name="src" value="magpie2_demo.qt.smil"/>
        <param name="autoplay" value="true"/>
        <param name="controller" value="true"/>
    
      <!--[if !IE]> <-->
    
        <<%%>object id="NonIEQT"
                data="magpie2_demo.qt.smil"
                type="video/quicktime"
                width="320"
                height="270">
           <param name="autoplay" value="-1"/>
           <param name="controller" value="-1"/>
        </object>
    
      <!--> <![endif]-->
    
    </object>
    

    Posted by Danny Tuppeny on

  5. [object-paranoia.php#comment5]

    Danny, that's pure genius, mate. I've updated the main article to include your findings, as that makes the graceful degradation example a lot less clumsy. Brilliant discovery!

    Posted by Gez on

  6. [object-paranoia.php#comment6]

    interesting but where I clan find a list of classid: values? i have searched in the net but I can't find anything

    Posted by Ernac on

  7. [object-paranoia.php#comment7]

    interesting but where I clan find a list of classid: values? i have searched in the net but I can't find anything

    The classid attribute is used to specify the URI for the object's location, such as a Python script or a Java class. The exact classid will depend on the object you want. It's also commonly used to display Microsoft ActiveX components, which are built to work with IE only. If that's what you're after, search for ActiveX objects, and you'll find loads.

    Posted by Gez on

Comments are closed for this entry.