Menu

XSLT, Browsers, and JavaScript

February 5, 2003

Bob DuCharme

Most XSLT processors offer some way to tell them: "here is the source document and here is the stylesheet to use when processing it." For a command-line XSLT processor, the document and stylesheet are usually two different parameters specified at the command line.

Web browsers, however, usually read a document from a web server and have no way to separately be told about the stylesheet to apply. To remedy this, the W3C Recommendation Associating Style Sheets with XML Documents describes a processing instruction to include at the beginning of a document to name a stylesheet to apply to that document. For example, the processing instruction in the following numbers.xml document tells an application to apply the stylesheet squareAsHTML.xsl to that document.

<?xml-stylesheet href="squareAsHTML.xsl" type="text/xsl" ?>
<numbers>
  <number>2</number>
  <number>11</number>
  <number>100</number>  
  <number>-5</number>
</numbers>

This processing instruction must be at the very beginning of a document, unless there is an XML declaration before it. Below is the squareAsHTML.xsl stylesheet referenced by the processing instruction above:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">
<!-- squareAsHTML.xsl: create an HTML document with a statement
     about the square of each "number" element read from the
     source tree. -->

  <xsl:template match="/"> <!-- Set up web page -->
    <html>
      <head>
        <title>Squares</title>
        <style> <!-- Put a little CSS in -->
           body { font-family: arial,helvetica; }
           h1   { font-size: 14pt }
           p    { font-size: 10pt}
        </style>
      </head>
      <body>
        <h1>Squares</h1>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="number">
    <xsl:variable name="value" select="."/>
    <p>
      <xsl:text>The square of </xsl:text>
      <xsl:value-of select="$value"/>
      <xsl:text> is </xsl:text>
      <xsl:value-of select="$value * $value"/>.</p>
  </xsl:template>

</xsl:stylesheet>

(If you're using XSLT to create result documents with processing instructions in them, see the earlier column XSLT, Comments and Processing Instructions for information on XSLT's xsl:processing-instruction instruction.) Using a command-line XSLT processor such as Saxon or Xalan to apply the squareAsHTML.xsl stylesheet to the numbers.xml document creates the following HTML document:

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <title>Squares</title><style> 
           body { font-family: arial,helvetica; }
           h1   { font-size: 14pt }
           p    { font-size: 10pt}
        </style></head>
   <body>
      <h1>Squares</h1>
      <p>The square of 2 is 4.</p>
      <p>The square of 11 is 121.</p>
      <p>The square of 100 is 10000.</p>  
      <p>The square of -5 is 25.</p>
   </body>
</html>

The best thing about specifying a document's stylesheet with this xml-stylesheet processing instruction is that it lets you use the document and designated stylesheet with a web browser. With the numbers.xml document and stylesheet shown above both sitting in the same directory of your hard disk, you can tell the current versions of Mozilla and Internet Explorer to open up numbers.xml and you'll see the result of the transformation. The rendered result should look the same as if you had opened an HTML file created by a command-line XSLT processor using the same input and stylesheet files. (The command-line processor Saxon does let you specify a command-line switch telling it to look for the stylesheet name in this processing instruction.)

You could also put the XML document and stylesheet on a web server, as I did here. Follow that link, and you should see the result of the stylesheet being applied to the XML document. After doing so, try a View Source, and you'll see the untransformed XML document with a slight change: I renamed the server copy of the stylesheet to squareAsHTML.xml, even though it's the exact same stylesheet file. As I write this, my web server's host provider ships XSLT stylesheets with a MIME type of text/html, so Mozilla treats them as HTML files. (I've suggested that they send them as text/xml.) When I give the stylesheet an extension of "xml", their server ships it with a MIME type of text/xml, so Mozilla knows that it's not HTML and uses its TransforMiiX XSLT processor to apply the stylesheet to the document and then renders the result in the browser.

As long as it's going to be rendered in a browser, you may as well take advantage of other features that modern browsers offer. My XSLT stylesheet above added a little CSS stylesheet to the rendered HTML to set the fonts of the output. By including JavaScript code with your result HTML, you can take advantage of client-side processing to add interactivity to the web pages that get created by your XSLT stylesheet.

Outputting HTML with JavaScript

The following stylesheet resembles the previous one, except that its head element has a script child element along with the title and style child elements. JavaScript code in this script element declares a function that displays a message box showing the square of the number passed to the function. If you pass it the number 3, the message box will say "the square is 9."

For each number element read from the input, instead of figuring out the square of the number, the second template rule adds a button to the form created by the first template rule. When clicked, this button calls the function declared in the script element.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">
<!-- squareAsHTMLJS1.xml: create an HTML document with JavaScript that
     interactively computes the square of each "number" element read from 
     the source tree. -->

  <xsl:template match="/"> <!-- Set up web page -->
    <html>
      <head>
        <title>Squares</title>
         <script language="JavaScript1.2">
           function showSquare(n) {
            alert("the square is " + n*n);
           }
        </script>
        <style> <!-- Put a little CSS in -->
           body { font-family: arial,helvetica; }
           h1   { font-size: 14pt }
           p    { font-size: 10pt}
        </style>
      </head>
      <body>
        <h1>Squares</h1>
        <p>Click a button to see the square of that number.</p>
        <form>
        <xsl:apply-templates/>
        </form>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="number">
    <p><input type="button" value=" {.} " onClick="showSquare({.})"/></p>
  </xsl:template>

</xsl:stylesheet>
    

Also in Transforming XML

Automating Stylesheet Creation

Appreciating Libxslt

Push, Pull, Next!

Seeking Equality

The Path of Control

To see this stylesheet in action, send your browser to this variation on the numbers.xml document from above. Its only difference from the original numbers.xml file is that it points to this new XSLT stylesheet. (Don't take my word for it -- do a View Source to see.) Again, the stylesheet has an extension of "xml" so that it works as well with Mozilla as it does with IE.

Putting a lot of JavaScript code inside of a script element in an XSLT template can get messy, so it's often convenient to store it in a separate file and then reference it with the script element's src attribute. This final variation on numbers.xml points to a stylesheet that does just that; it looks like the stylesheet above, but its script element has an src attribute that points to a squareAsHTMLJS2.js file. The showSquare() code is stored in squareAsHTMLJS2.js instead of being between the <script></script> tags in the XSLT stylesheet.

Keeping your JavaScript code in a separate file is cleaner, but putting it between the <script></script> tags gives you a powerful opportunity: you don't have to hardcode your JavaScript code in your XSLT stylesheet; you can write XSLT logic to generate JavaScript code based on dynamic conditions such as values in your input. Next month, we'll see how this can let you turn nested a elements in an XHTML source document into one-to-many links implemented as pop-up menus in the rendered result.