Browser-Side XSLT - Ajax Patterns

Browser-Side XSLT

From Ajax Patterns

Evidence: 2/3

Tags: Presentation Render Style Stylesheet Transform View XML XPath XSLT


Contents

In A Blink

Diagram Doc + XSLT = HTML ok


Technical Story

Dave has just received a request to change the weather portlet on the homepage. The image must now be on the bottom, not the top, and some of the wording is apparently unclear. The browser receives periodic weather updates in the form of an XML specification and uses Browser-Side XSLT to get HTML for the portlet. The stylesheet is embedded inside the static homepage HTML file. So Dave just has to tweak the XSLT to get all the browsers rendering the new design.


Problem

How can you render incoming XML?


Forces

  • Many Ajaxian applications receive XML Messages and have to convert them into HTML.
  • While modern browsers provide good support for XML parsing via the DOM, the direct interface is lengthy and tedious to use.
  • XML support varies from browser to browser. Directly parsing XML is fragile.
  • Code gets complex and error-prone when you mix HTML generation with application logic. Javascript is not well-suited to programmatically building up HTML strings from input XML.


Solution

Apply XSLT to convert XML Messages into XHTML. XSLT - Extensible Stylesheet Langage Transformations - is a language for transforming XML documents into other forms. Here, XML is transformed to XHTML, a type of HTML that conforms to the XML standard. (You could get away with converting to plain-old HTML, but things works better if you stick to XHTML.) XSLT is fairly well-supported in modern browsers and, although browser APIs vary, there are good cross-browser toolkits available.

XSLT is well-suited to rendering XML. An XML document is designed to encapsulate a data object, and an XSLT stylesheet is a strategy for presenting such objects. Previously, XSLT has been used as a server-side technology - the server grabs some XML and creates a web page by transforming it into XHTML. More recently, browsers have incorporated XSLT functionality, so the browser can create an HTML page by marrying together an XML document with an XSLT stylesheet.

Browser-Side XSLT is slightly different. Here, the XML document does not constitute the contents of the entire page, but a response from an XMLHttpRequest Call. The transformation to XHTML can take advantage of the browser's built-in XSLT support, or alternatively an XSLT processor can be implemented in Javascript, building on the browser's more primitive XML features.

However the XSLT occurs, you're unlikely to be implementing it yourself. There are a couple of good cross-browser libraries available, discussed in the Real-World Examples below.

It's beyond the scope of this pattern to explain XSLT in any detail; however, here is a quick overview:

  • An XSLT stylesheet is itself an XML document.
  • XSLT expressions are specified in another language, XPath. Among its capabilities, XPath provides a powerful mechanism for expressing a position within an XML document. Consider the Refactoring Illustration below, which contains a reference to /category/items/category. This matches an element like this:
  <category>
    <items>
      <category name="popular">
        <!-- The XPath expression matches this category node -->
        ...
      </category>
      <category name="extras">
        <!-- The XPath expression matches this category node -->
        ...
      </category>
    </items>
  </category>
  • A stylesheet is composed of rules. Each rule has a pattern which defines when it applies, and a template which defines what to output. Continuing with the example, here's a full rule:
  <xsl:template match="/category/items/category">
      <div class="category"
           onclick="retrieveCategory('{@name}')">
           <xsl:value-of select="@name"/>
      </div>
  </xsl:template>

When each node is reached, the template body is outputted. @name refers to the name attribute on the category tag. So when the processing engine reaches the following XML segment:

  <category name="extras">

the following HTML will be output:

  <div class="category"
       onclick="retrieveCategory('{extras}')">
       extras
  </div>

This discussion has focused on the most obvious application of Browser-Side XSLT: conversion to HTML for display to the user. You can also convert the XML to a Javascript too, and then execute it with the eval() method. For example, it would be possible to build a native Javascript object by converting some XML into some code that creates the object.


Decisions

How will you obtain the XSLT stylesheet?

Any Browser-Side XSLT requires two things:

  • An XML document.
  • An XSLT stylesheet.

Both are usually passed in to the XSLT processor as plain strings representing the entire document (as opposed to URLs, say). The XML document comes from an XMLHttpRequest Call, but where does the XSLT stylesheet come from? You have a few options:

  • Store it server-side: You can hold the file server-side, and then use an independent XMLHttpRequest Call to retrieve it. In this case, you'll probably want to keep a local copy in the browser for later use. Also, if you use an asynchronous call, there's a potential race condition: you need to ensure the stylesheet is retrieved before any transformation takes place. The Refactoring Illustration below demonstrates this approach.
  • Hand-code it as a Javascript string: You can build up a string in Javascript, though this leads to messy code which blurs the distinction between logic and presentation. There is one benefit of this approach: the stylesheet is dynamic, so could potentially vary according to the current context.
  • Store it inside the initial HTML document You can tuck the stylesheet somewhere inside the initial HTML document where it won't be seen by the user. For example:
    • Make it the content of an invisible textarea.
    • Store it in an XML Data Island within document.

Texto en negrita editado




Real-World Examples

Freja

Freja is an javascript application framework, which supports the MVC paradigm. It makes use of clientside XSLT transformation to generate view markup.

Kupu

Kupu is an online word-processor which stores content in XML and renders it using browser-side XSLT, using the Sarissa framework described below.

Sarissa Framework

Sarissa is an open-source, cross-browser, framework for all things XML. XSLT is supported as well as XML parsing, XPath queries, and XMLHttpRequest invocation.

Google AJAXSLT Framework

Google AJAXSLT is Google's open-source framework for cross-browser XSLT and XPath.

Losru Advertisng Portal

Losru is a production grade content management system based on pure server side XML - XSLT transformation.

XSLTForms

XSLTForms supports the MVC paradigm and uses inline XML markup conforming to the XForms Recommendation. It makes use of clientside or serverside XSLT transformation to generate view markup.


Refactoring Illustration

XSLT Drilldown Demo

This refactoring is similar to that performed in Browser-Side Templating, but using XSLT instead of templating. The starting point, the Drilldown Demo, converts XML to HTML using a series of Javascript string concatenations. The callback function, which receives the XML, therefore performs lots of string concatenations, like follows:

  html+="<div id='categoryName'>" + categoryName + "</div>";

In this refactoring, all that string-handling is replaced by an XSLT transformation, using the Sarissa library. One slight complication is the stylesheet - how does the script access it? The solution here is to keep it as a separate file on the server and pull it down on page load.

 var xsltDoc;
 window.onload = function() {
   ...
   ajaxCaller.getXML("./drilldown.xsl",function(response) {xsltDoc = response;});
   ...
 }

As was mentioned in the Decisions above, there's a race condition here, because the XML response may come before the stylesheet. To deal with this, the drilldown callback function keeps looping until the stylesheet is defined.

 function onDrilldownResponse(xml) {
   if (xsltDoc==null) {
     setTimeout(function() { onDrilldownResponse(xml); }, 1000);
     return;
   }
   ...
 }

Beyond that, the callback function is simply an invocation of Sarissa's XSLT processor.

 function onDrilldownResponse(xml) {
   ...
   var xsltProc  = new XSLTProcessor();
   xsltProc.importStylesheet(xsltDoc);
   var htmlDoc = xsltProc.transformToDocument(xml);
   var htmlString = Sarissa.serialize(htmlDoc);
   $("drilldown").innerHTML = htmlString;
 }

The only thing left is the stylesheet itself, shown below. I'll spare a walkthrough. You can compare it with the plain Javascript handling in the Drilldown demo or use a DOM Visualisation tool to see the output it produces.

  <?xml version="1.0"?>
  <xsl:stylesheet version="1.0"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
      <html>
        <body>
          <xsl:apply-templates/>
        </body>
      </html>
    </xsl:template>

    <xsl:template match="/category">
      <div id='categoryName'><xsl:value-of select="@name" /></div>
      <xsl:if test="@parent!=''">
        <div id='parent' onclick='retrieveCategory("{@parent}")'>
          Back to <br/><xsl:value-of select="@parent" />
        </div>
      </xsl:if>
      <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="/category/items/link">
       <div class="link"><a href="{url}"><xsl:value-of select="name"/></a></div>
    </xsl:template>

    <xsl:template match="/category/items/category">
        <div class="category"
             onclick="retrieveCategory('{@name}')">
             <xsl:value-of select="@name"/>
        </div>
    </xsl:template>

  </xsl:stylesheet>


Alternatives

Browser-Side Templating

Browser-Side Templating is a solution for rendering arbitrary content, including XML. Browser-Side XSLT allows for more powerful transformations and avoids the cumbersome manipulation of XML in Javascript. The main downside is simply the skill set: Browser-Side Templating just builds on existing Javascript knowledge, whereas XSLT is a completely separate language.


Related Patterns

XML Message

Browser-Side XSLT is driven by the need to deal with incoming XML Messages.

Semantic Response

The importance of XSLT mostly arises from the need to deal with XML Messages passed back as Semantic Responses.

XML Data Island

Browser-Side XSLT can be used to render an element contained in an XML Data Island. Also, XML Data Island is a good place to stick an XSLT stylesheet.


Visual Metaphor

Browser-Side XSLT is a strategy for presenting abstract data in a suitable human form. People do this all the time with diagrams. For instance, a family tree is one way to render the abstract set of relationships in a family.