AjaxPatterns
This is the original book draft version for the XML Data Island Ajax pattern, offered for historical purposes and as an anti-spam measure.
You're probably better off visiting the Live Wiki Version for XML Data Island instead, where you can make changes and see what others have written.

XML Data Island

Data, DOM, Storage, XBL, XML

Developer Story

Dave's demographics viewer stores each table of statistics as an XML document. A big cache of XML documents is held within the page, one for each table. The user can rearrange the tables, and switch them between visible and hidden, but all the XML metadata remains on the page. To render the tables, Dave exploits the browser's ability to translate XML into HTML tables.

Problem

How can you render incoming XML and retain the data?

Forces

  • Many Ajax apps receive “XML Message”s and must convert them into HTML.

  • The incoming data often has to be retained as well.

Solution

Retain XML responses as "XML Data Islands" - nodes within the HTML DOM. Strictly speaking, an XML Data Island is an XML document embedded in a standard XHMTL document, usually within an <xml> tag. Say the initial page looks like this:

    <html>
      <head>
        ...
      </head>
      <body>
        <h1>Top Score</h1>
        <p>Here's the ... </p>
        ...
        <xml id="scoreData"></xml>
      </body>
    </html>

After an “XMLHttpRequest Call” brings the browser an “XML Message”, the message is retained in the tag, using techniques described in a moment. The result is a web page DOM like this:

    <html>
      <head>
        ...
      </head>
      <body>
        <h1>Top Scores</h1>
        <p>Here's the ... </p>
        ...
        <xml id="scoreData">
          <score>
            <title>Galaga"</title>
            <player>AAA</player>
            <score>999610</score>
          <score>
        </xml>
      </body>
    </html>

How do you do something like that in Javascript? You might assume it's pretty trivial, given that HTML itself is XML (or close enough to it). But unfortunately, it's not as easy as you'd expect and also browser-dependent. If you have the XML string, you can set scoreData element's innerHTML become the string value. If you only have a DOM node, you can convert it to an XML string by inspecting its innerHTML property. However, this won't work on all browsers and might lead to portability problems as browsers interpret the stirring in different ways. An alternative is to avoid stirng manipulation and directly graft one DOM node onto another. The document object has two useful methods here: cloneNode() and importNode(). Again, there are plenty of portability issues to consider - see Peter-Paul Koch's discussion. In most cases, the easiest, most portable, solution will be to use a library like Sarissa.

In a more general sense, this pattern involves retaining “XML Message”s regardless of how you store them. While the DOM is a convenient

How do you do something like that in Javascript? You might assume it's pretty trivial, given that HTML itself is XML (or close enough to it). But unfortunately, it's not as easy as you'd expect and also browser-dependent. If you have the XML string, you can set scoreData element's innerHTML become the string value. If you only have a DOM node, you can convert it to an XML string by inspecting its innerHTML property. However, this won't work on all browsers and might lead to portablity problems as browsers interpret the stirng in different ways. An alternative is to avoid stirng manipulation and directly graft one DOM node onto another. The document object has two useful methods here: cloneNode() and importNode(). Again, there are plenty of portability issues to consider - see Peter-Paul Koch's discussion. In most cases, the easiest, most portable, solution will be to use a library like Sarissa.

place for storage, and has some value-add described below, you could also save the XML in a normal Javascript variable. The key characteristic of the pattern is that you're retaining the “XML Message” and using it as a data structure, rather than transforming it into a custom Javascript object.

It's easy enough to do this, but what's the point? Here are three applications:

  • Transforming XML Data Islands to and from HTML. (Note: this is browser-specific.)

  • Retaining XML for later use.

  • Including an XML document on initial page load.

Transforming XML Data Islands to and from HTML. XML Data Islands are traditionally an IE-specific capability. From IE5, you can tie HTML controls to an embedded XML document. When the XML data changes, the HTML is also updated. And when the HTML changes - if the control is mutable - the XML is updated. In IE, to tie an HTML element to a data island, you indicate the XML data island, preceded by a # and the field within that island. So to create an input field based on the player field above:

  <input type="text" datasrc="#scoreData" datafld="player">

Now, this is an IE-specific function, but Firefox has its own alternative: eXtensible Binding Language (XBL). The idea is similar, but the updating behaviour isn't automatic. With Mozilla, you have to create an XBL document that explicitly states how HTML controls relate to the XML.

Even without these browser-specific capabilities, it's still fairly straightforward to make the transformation using a technology like “Browser-Side XSLT”.

Retaining XML for Later Use. Sometimes it's useful to retain the response for later use. For example, you can keep it to build up a “Browser-Side Cache”. Another application is for validation - the browser might receive a raw XML document containing some form fields, then augment it with user responses before uploading it back to the server.

To store a response for later use, you could wrap it into a custom data structure. But that requires some coding effort and also complicates things a bit by adding a new data structure. When the response is an “XML Message”, you have an alternative: just keep the XML. While it may not be the optimal format, it's still a structured format which you can query quite easily.

Including an XML Document on Initial Page Load. The initial page load sequence sometimes requires an XML document. For example, the XML document might describe the initial data to be viewed or it might be a stylesheet for “Browser-Side XSLT”. You could go through the motions of extracting it with a further “XMLHttpRequest Call”, but it would be faster to load the XML as part of the initial page load. An XML Data Island is an easy way to do this: just output the XML content wrapped inside an <xml> tag. Alternatively, IE lets you specify a URL in the src attribute.

Real-World Examples

PerfectXML Demo

Darshan Singh's IE demo is explained in the corresponding PerfectXML article. It's a straightforward table, populated by an XML Data Island embedded in the corresponding HTML. The demo only works in IE.

Mozilla.org Demo

Thad Hoffman's Mozilla demo is explained in a corresponding mozilla.org article. It simulates IE's behaviour, using standard XML parsing in Javascript, to convert XML to HTML. The demo only works in Mozilla or Firefox.

TechRepublic Demo

Philip Perkin's example explains how to use Mozilla's answer to XML Data Islands, eXtensible Binding Language (XBL) (Figure 1.32, “XBL Demo”). The code's online and there's a demo available.

Figure 1.32. XBL Demo

XBL Demo

Code Refactoring [AjaxPatterns XML Data Island Sum]

This example adds a very simple caching mechanism to the Basic Sum Demo. The most recent XML response is retained as an “XML Data Island”, so if the user tries to resubmit the query, no server call occurs. In the absence of an “XML Data Island”, the script would need to retain the call and response in a custom data format. But retaining it as XML means no data format has to be created. Instead of manually navigating the document with the standard DOM API, the Interactive Website Framework (IWF) library is used. IWF lets you treat the DOM object more like a custom data structure - as the code below shows, it lets you drill down using tag names rather than generic XML names.

Some minor changes are made to the XML format for easier parsing, leading to the following data format:

    <sum>
      <inputs>
        <figure1>4</figure1>
        <figure2>6</figure2>
        <figure3></figure3>
      </inputs>
      <outputs>10</outputs>
    </sum>

To the initial HTML, a placeholder has been introduced to retain the most recent XML response - the XML Data Island:

    <xml id="sumResponse"></xml>

The xml is retrieved as a plain-text document and IWF used to transform it into a special DOM-like format for convenient parsing. The IWF document is interrogated to get the sum, which is injected onto the DOM status element as before. Finally, the XML Data Island is populated with the XML response, in its plain-text format (as opposed to a DOM object). The XML response includes the input figures as well as the resulting sum, so we'll be able to use it later on to decide if the figures have changed since the last response.

    function onSumResponse(xml, headers, callingContext) {
      var doc = new iwfXmlDoc(xml);
      $("sum").innerHTML = doc.sum.outputs[0].getText()
      $("sumResponse").innerHTML = xml;
    }

The XML Data Island is used to decide whether a call is necessary. To make the effect more visible, the status area is blanked as soon as the user clicks submit. It's re-populated with the retained value if it turns out the current data was the same as the previous query:

    function submitSum() {
      $("sum").innerHTML = "---";
      var figures = {
        figure1: $("figure1").value,
        figure2: $("figure2").value,
        figure3: $("figure3").value
      }
      var sumResponseXML = $("sumResponse").innerHTML;
      if (sumResponseXML!="") {
        doc = new iwfXmlDoc(sumResponseXML);
        var alreadyStoredInDOM = (
             figures.figure1 == doc.sum.inputs[0].figure1[0].getText()
          && figures.figure2 == doc.sum.inputs[0].figure2[0].getText()
          && figures.figure3 == doc.sum.inputs[0].figure3[0].getText()
        );
        if (alreadyStoredInDOM) {
          // No need to fetch - just retrieve the sum from the DOM
          $("sum").innerHTML = doc.sum.outputs[0].getText();
          return;
        }
      }
      ajaxCaller.get("sumXML.phtml", figures, onSumResponse, false, null);
    }

Alternatives

“Browser-Side XSLT” is a more general way to transform XML into HTML. In addition, the patterns are related insofar as “Browser-Side XSLT” can use an XML Data Island to store stylesheets.

“Browser-Side Templating” is a general technique for converting XML, or other data formats, into HTML.

Metaphor

The name itself is the metaphor: an island of data amid a pool of HTML content.

Live Wiki Version for XML Data Island