11.1. XML Data IslandData, DOM, Storage, XBL, XML Figure 11-1. XML Data Island11.1.1. Developer StoryDave'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. 11.1.2. ProblemHow can you render incoming XML and retain the data? 11.1.3. Forces
11.1.4. SolutionRetain XML responses as XML Data Islandsnodes within the HTML DOM. Strictly speaking, an XML Data Island is an XML document embedded in a standard XHTML 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 ></xml> </body> </html> After an XMLHttpRequest Call (Chapter 6) brings the browser an XML Message (Chapter 9), 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 > <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 is browser-dependent. If you have the XML string, you can set scoreData element's innerHTML as the string value. If you have only 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 string in different ways. An alternative is to avoid string 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 considersee Peter-Paul Koch's discussion (http://www.quirksmode.org/blog/archives/2005/12/xmlhttp_notes_c.html). In most cases, the easiest, most portable, solution will be to use a library like Sarissa (http://sarissa.sourceforge.net). In a more general sense, this pattern involves retaining XML Messages regardless of how you store them. While the DOM is a convenient place for storage and has some value-added features 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:
11.1.5. Real-World Examples11.1.5.1. PerfectXML DemoDarshan Singh's IE demo (http://www.perfectxml.com/articles/xml/islandex.htm) is explained in the corresponding PerfectXML article (http://www.perfectxml.com/articles/xml/msxml30.asp). It's a straightforward table, populated by an XML Data Island embedded in the corresponding HTML. The demo works in IE only. 11.1.5.2. Mozilla.org demoThad Hoffman's Mozilla demo (http://www.mozilla.org/xmlextras/xmldataislands/example1.html) is explained in a corresponding mozilla.org article (http://www.mozilla.org/xmlextras/xmldataislands/): corresponding mozilla.org article. It simulates IE's behavior, using standard XML parsing in JavaScript, to convert XML to HTML. The demo works only in Mozilla or Firefox. 11.1.5.3. TechRepublic demoPhilip Perkin's example explains how to use Mozilla's answer to XML Data Islands, eXtensible Binding Language (XBL); see Figure 11-2 (http://techrepublic.com.com/5100-3513_11-5810495.html). The code's online and there's a demo available (http://www.phillipweb.com/mozilla/mozilla_xbl.htm). Figure 11-2. XBL demo11.1.6. Code Refactoring: AjaxPatterns XML Data Island SumThis example adds a very simple caching mechanism to the Basic Sum Demo (http://ajaxify.com/run/sum). 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 (http://iwf.sourceforge.net) is used. IWF lets you treat the DOM object more like a custom data structureas the following code 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 (http://ajaxify.com/run/sum/xml/dataIsland/sumXML.php?figure1=4&figure2=6): <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 responsethe XML Data Island: <xml ></xml> The XML is retrieved as a plain-text document and IWF is 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 whether 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 repopulated with the retained value if it turns out the current data is 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); } 11.1.7. Alternatives11.1.7.1. Browser-Side XSLTBrowser-Side XSLT (see the next pattern) 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. 11.1.7.2. Browser-Side TemplatingBrowser-Side Templating (see later) is a general technique for converting XML or other data formats into HTML. 11.1.7.3. Browser-Side CacheXML Data Island can be used to store a Browser-Side Cache (Chapter 13). 11.1.8. MetaphorThe name itself is the metaphor: an island of data amid a pool of HTML content. |