October 27th, 2005 by Nick Fitzsimons
Zen gardener Dave Shea’s post Bye Bye Tan Hack attracted a number of comments from people claiming that Internet Explorer’s conditional comments (CCs) were ungeneratable if you are using XSLT to produce your pages. “Strange,” I thought, “I’ve done that before.”
I’ve been working with XSLT for several years, and the project I’m currently working on uses it intensively; getting CCs working was simmering somewhere down my todo list. (I couldn’t recycle any previous efforts for boring, legal IP reasons.) So I set to work, and am happy to present an XSLT template which will build your conditional comments for you. I’ve tested it with Xalan 2.2, Saxon 6.5.3, MSXML 3 and MSXML 4 (those being the only XSLT processors I had to hand), and it worked perfectly with all of them.
In addition, I’ve created a demonstration for Firefox, Internet Explorer and Safari (2.0.1 on Tiger, anyway). This XML file uses an XSL stylesheet which generates a conditional comment. Using Firefox’s DOM Inspector you can see that the comment is created correctly. Using Internet Explorer, a stylesheet is linked which modifies the appearance of the page’s heading. This demonstrates that the technique I’m describing can be used client-side as well as in server-side transformations.
First, the template:
<xsl:template name="conditionalComment"> <xsl:param name="qualifier" select="'IE'"/> <xsl:param name="contentRTF" select="''" /> <xsl:comment>[if <xsl:value-of select="$qualifier"/>]<![CDATA[>]]> <xsl:copy-of select="$contentRTF" /> <![CDATA[<![endif]]]></xsl:comment> </xsl:template>
Easy, yes? Well, maybe not; let’s look at it one step at a time.
The template takes two parameters:
- The qualifier for the “if” clause of the comment. Defaults to “IE”, which targets any version of Internet Explorer that recognises conditional comments.
- The body of the comment: the stuff that IE gets to see (depending on the qualifier). This is passed as a result tree fragment; if you’re not sure what that means, you may want to go and read the relevant section of the XSLT spec. Defaults to an empty string.
We can invoke the template using the following code:
<xsl:call-template name="conditionalComment"> <xsl:with-param name="qualifier" select="'lte IE 6'"/> <xsl:with-param name="contentRTF"> <link rel="stylesheet" type="text/css" href="ie-win-fixup.css" /> </xsl:with-param> </xsl:call-template>
That will give us a conditional comment targeting Internet Explorer versions 6 and below (“lte IE 6″). NOTE: I haven’t messed up my character entities there; the HTML we want to output really is expressed as you see above in the XSLT code.
So how does it work?
The comment is generated using the
- Starts outputting our comment to the result tree. Output so far:
[if <xsl:value-of select="$qualifier"/>]
- Starts adding on the bits needed to make IE parse this as a conditional comment, using the value of the qualifier parameter. In the example case, our output is now up to
<!--[if lte IE 6]
- This CDATA section is treated as raw characters to copy to the result tree by the XSLT processor. So our output becomes:
<!--[if lte IE 6]>which is the start of our conditional comment.
- This just copies the contents of our result tree fragment to the result tree. Our output is now:
<!--[if lte IE 6]> <link rel="stylesheet" type="text/css" xhref="ie-win-fixup.css" mce_href="ie-win-fixup.css" />
Notice that the character entity references we passed in in contentRTF (
>) have now been turned into the actual characters we wanted, through the magic of result tree fragments. (In fact, this technique can be abused to allow invalid XML output from an XSLT processor, which is why it isn’t likely to survive into XSLT 2.0.)
- Our final line uses another CDATA section to provide the markup IE needs to recognise the end of the comment, and then the
closes our comment. Note again that there is no whitespace between the CDATA section and the closing-comment element: this is where IE’s CC parser might trip up, so we make do with a little unreadability for the sake of getting the result we want which, in all its refulgent splendour, now looks like:
<!--[if lte IE 6]> <link rel="stylesheet" type="text/css" href="ie-win-fixup.css" /> <![endif]-->
So there you have it: an XSLT template for creating Internet Explorer conditional comments. Hopefully re-targeting your CSS hacks in readiness for the new age just got a little easier.