Home arrow JavaScript arrow Page 2 - More on JavaScript and XML

More on JavaScript and XML

In this conclusion to a three-part series, you'll learn about serializing XML, expanding HTML templates with XML data, and more. This article is excerpted from chapter 21 of JavaScript: The Definitive Guide, Fifth Edition, written by David Flanagan (O'Reilly; ISBN: 0596101996). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Author Info:
By: O'Reilly Media
Rating: 4 stars4 stars4 stars4 stars4 stars / 9
August 16, 2007
  1. · More on JavaScript and XML
  2. · 21.6 Expanding HTML Templates with XML Data
  3. · 21.7 XML and Web Services
  4. · 21.8 E4X:ECMAScript for XML

print this article

More on JavaScript and XML - 21.6 Expanding HTML Templates with XML Data
(Page 2 of 4 )

One key feature of IE’s XML data islands is that they can be used with an automatic templating facility in which data from a data island is automatically inserted into HTML elements. These HTML templates are defined in IE by adding datasrc and datafld (“fld” is short for “field”) attributes to the elements.

This section applies the XML techniques seen earlier in the chapter and uses XPath and the DOM to create an improved templating facility that works in IE and Firefox. A template is any HTML element with a datasource attribute. The value of this attribute should be the ID of an XML data island or the URL of an external XML doc ument. The template element should also have a foreach attribute. The value of this attribute is an XPath expression that evaluates to the list of nodes from which XML data will be extracted. For each XML node returned by the foreach expression, an expanded copy of the template is inserted into the HTML document. The template is expanded by finding all elements within it that have a data attribute. This attribute is another XPath expression to be evaluated in the context of a node returned by the foreach expression. This data expression is evaluated with XML.getNode() , and the text contained by the returned node is used as the content of the HTML element on which the data attribute was defined.

This description becomes much clearer with a concrete example. Example 21-12 is a simple HTML document that includes an XML data island and a template that uses it. It has an onload() event handler that expands the template.

Example 21-12. An XML data island and HTML template

<!-- Load our XML utilities for data islands and templates -->
<head><script src="xml.js"></script></head> <!-- Expand all templates on document load
<body onload="XML.expandTemplates()">

<!-- This is an XML data island with our data -->
<xml id="data" style="display:none"> <!-- hidden with CSS -->
    <contact name="Able Baker"><email>able@example.com</email></contact>
    <contact name="Careful Dodger"><email>dodger@example.com </email></contact>
    <contact name="Eager Framer"><email>framer@example.com </email></contact>

<!-- These are just regular HTML elements
<table> <tr><th>Name</th><th>Address</th></tr>
<!-- This is a template. Data comes from the data island with id "data". -->
<!-- The template will be expanded and copied once for each <contact> tag -->
<tr datasource="#data" foreach="//contact"> <!-- The "name" attribute of the <contact> is inserted into this element -->
<td data="@name"></td>
<!-- The content of the <email> child of the <contact> goes in here -->
<td data="email"></td>
</tr> <!-- end of the template -->

A critical piece of Example 21-12 is the onload event handler, which calls a function named XML.expandTemplates() . Example 21-13 shows the implementation of this function. The code is fairly platform-independent, relying on basic Level 1 DOM functionality and on the XPath utility functions XML.getNode() and XML.getNodes() defined in Example 21-10.

Example 21-13. Expanding HTML templates

* Expand any templates at or beneath element e.
* If any of the templates use XPath expressions with namespaces, pass
* a prefix-to-URL mapping as the second argument as with XML.XPathExpression()
* If e is not supplied, document.body is used instead. A common
* use case is to call this function with no arguments in response to an
 * onload event handler. This automatically expands all templates.
XML.expandTemplates = function(e, namespaces) {
    // Fix up arguments a bit.
    if (!e) e = document.body;
    else if (typeof e == "string") e = document.getElementById(e);
    if (!namespaces) namespaces = null; // undefined does not work

    // An HTML element is a template if it has a "datasource" attribute.
    // Recursively find and expand all templates. Note that we don't
    // allow templates within templates.
    if (e.getAttribute("datasource")) {
// If it is a template, expand it.
XML.expandTemplate(e, namespaces);
    else {
// Otherwise, recurse on each of the children. We make a static
        // copy of the children first so that expanding a template doesn't
        // mess up our iteration.
        var kids = []; // To hold copy of child elements
        for(var i = 0; i < e.childNodes.length; i++) {
var c = e.childNodes[i];
            if (c.nodeType == 1) kids.push(e.childNodes[i]);

        // Now recurse on each child element
        for(var i = 0; i < kids.length; i++)
            XML.expandTemplates(kids[i], namespaces);

* Expand a single specified template.
 * If the XPath expressions in the template use namespaces, the second
*argument must specify a prefix-to-URL mapping
XML.expandTemplate = function(template, namespaces) {
    if (typeof template=="string") template=document.getElementById(template);
    if (!namespaces) namespaces = null; // Undefined does not work

    // The first thing we need to know about a template is where the
    // data comes from.
    var datasource = template.getAttribute("datasource");

    // If the datasource attribute begins with '#', it is the name of
    // an XML data island. Otherwise, it is the URL of an external XML file.
    var datadoc;
    if (datasource.charAt(0) == '#') // Get data island
datadoc = XML.getDataIsland(datasource.substring(1));
    else                 // Or load external document
        datadoc = XML.load(datasource);

    // Now figure out which nodes in the datasource will be used to
    // provide the data. If the template has a foreach attribute,
    // we use it as an XPath expression to get a list of nodes. Otherwise,
    // we use all child elements of the document element.
    var datanodes;
    var foreach = template.getAttribute("foreach");
    if (foreach) datanodes = XML.getNodes(datadoc, foreach, namespaces);
    else {
// If there is no "foreach" attribute, use the element
        // children of the documentElement
        datanodes = [];
        for(var c=datadoc.documentElement.firstChild; c!=null; c=c.nextSibling)
if (c.nodeType == 1) datanodes.push(c);

    // Remove the template element from its parent,
    // but remember the parent, and also the nextSibling of the template.
    var container = template.parentNode;
    var insertionPoint = template.nextSibling;
    template = container.removeChild(template);

    // For each element of the datanodes array, we'll insert a copy of
    // the template back into the container. Before doing this, though, we
    // expand any child in the copy that has a "data" attribute.
for(var i = 0; i < datanodes.length; i++) {
        var copy = template.cloneNode(true);                     // Copy template
        expand(copy, datanodes[i], namespaces);                // Expand copy
        container.insertBefore(copy, insertionPoint);            // Insert copy

    // This nested function finds any child elements of e that have a data
    // attribute. It treats that attribute as an XPath expression and
    // evaluates it in the context of datanode. It takes the text value of
    // the XPath result and makes it the content of the HTML node being
    // expanded. All other content is deleted.
    function expand(e, datanode, namespaces) {
for(var c = e.firstChild; c != null; c = c.nextSibling) {
            if (c.nodeType != 1) continue;  // elements only
            var dataexpr = c.getAttribute("data");
            if (dataexpr) {
                // Evaluate XPath expression in context.
                var n = XML.getNode(datanode, dataexpr, namespaces);
                // Delete any content of the element
                c.innerHTML = "";
                // And insert the text content of the XPath result
            // If we don't expand the element, recurse on it.
            else expand(c, datanode, namespaces);

    // This nested function extracts the text from a DOM node, recursing
    // if necessary.
    function getText(n) {
switch(n.nodeType) {
case 1: /* element */
var s = "";
for(var c = n.firstChild; c != null; c = c.nextSibling)
s += getText(c);
return s;
case 2: /* attribute*/
case 3: /* text */
case 4: /* cdata */
return n.nodeValue;
            return "";


blog comments powered by Disqus

- Project Nashorn to Make Java, JavaScript Wor...
- JavaScript Virus Attacks Tumblr Blogs
- Google Releases Stable Dart Version, JavaScr...
- Khan Academy Unveils New JavaScript Learning...
- Accessing Nitro? There`s an App for That
- JQuery 2.0 Leaving Older IE Versions Behind
- Fastest JavaScript Engine Might Surprise You
- Microsoft Adjusting Chakra for IE 10
- Brendan Eich: We Don`t Need Google Native Cl...
- An Overview of JavaScript Statements
- An Overview of JavaScript Operators
- Overview of JavaScript Variables
- More of the Top jQuery Social Plugins
- The Top jQuery Social Plugins
- More of the Top jQuery Slider Plugins

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 

Developer Shed Affiliates


© 2003-2018 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials