RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Make Data Islands Work in All Browsers : Page 3

XML Data Islands and XML Data Sources aren't a new idea—and they are no longer exclusive to Internet Explorer, either. Here's how to use data islands generically, without getting locked in to any one vendor's implementation, and make your data-centric Web pages work across all modern browsers.

Planning the Script
To implement data binding, you can use a DHTML technique—a script that manipulates the document content. The days of incomprehensible spaghetti-like DHTML are gone now—standards are firmly in place, and DHTML code is expected to be very clean. As a minimum, all the code should be neatly encapsulated in a JavaScript object where it's kept tidily separate from other code. Here's the skeleton of the data-binding object, which will be nearly the entire content of islands.js:

   var islands = {
     isMSIE : ...,
     getElementsByAttribute : 
        function (node, att) { ...},
     getEBArecursive : 
       function (list, node, att) { ...},
     getFieldDataFromRecord : 
       function (rec,field) { ... },
     makeIEtree : function (island) { ... },
     merge : function (target, template, record) { ... },
     bind : function () { ... }
This is a literal object (similar to a Perl hash) that consists of seven properties: a simple value, isMSIE, and six anonymously defined functions. Each function is assigned to a property, so each property acts as an object method. Anonymous functions specified like this are useful because they appear only in the object where they're defined. That means this object can be included in any Web page without fear of clashing with someone else's function names. Only the island's variable name needs to be unique.

To complete this object requires only replacing the ellipses with code. But before doing that, here's an explanation of what each property does.

  • isMSIE is simple; it's just a browser check flag.
  • getElementsByAttribute() scans a document for nodes that have a specific attribute. Because the DOM standards don't offer such a function, one's been made up.
  • getEBArecursive() is a recursive version of getElementsByAttribute() that reduces the amount of code required.
  • getFieldDataFromRecord() extracts a datafield item from the data island.
  • makeIEtree() fixes some broken behavior in IE—you'll see how shortly.
  • merge() takes the page content, a special template, and the data island data, and creates the final content.
  • bind() is the first thing called; it's what makes everything happen.
Now, you have to make the binding happen. You do that by adding an onload event handler to the end of islands.js:

   window.onload = function () { islands.bind(); };
There's another fancy JavaScript feature at work in this line: scope chains. It's best not to use this simpler syntax:

   window.onload = islands.bind();
If you use the simple syntax, when the bind() method runs, the current object is the window object. But calling the bind() method from its own object, inside an anonymous function, places the island's object "in scope." That way, it's easy to refer to other methods in the islands object using the special this property, which saves you from having to pollute the object methods with references to the islands variable.

Planning the Object Methods
Here's how binding is implemented. First, the unfortunate browser check is trivial, so let's get that out of the way:

The bind() method does all the work. For planning purposes, here's a list of the tasks the method must accomplish:

  1. Find all the datasource targets. For each one:
  2.   Extract the target's existing content into a separate template
  3.   If it's IE, repair the matching data island
  4.   Find each record in the matching data island, and for each one:
  5.     Merge the record and the template
  6.     Copy the merged template back into the page.
Some of the methods required are quite general; you can find such things scattered all over the Web in existing pages and in the blogs of leading Web thinkers. Here's an implementation. First, the getElementsByAttribute() method, which is just a wrapper around the recursive version:

     getElementsByAttribute : function (node, att) {
       var rv = [];
       this.getEBArecursive(rv, node, att);
       return rv;
Given any DOM node and an attribute name, it returns all matching DOM nodes underneath ("inside") that node in an array. Here's the part that does the work:

     getEBArecursive : function (list, node, att) {
       for (var i=node.childNodes.length-1; i>=0; i--)
         var child = node.childNodes.item(i);
         if ( child.nodeType == 1 ) {
           if ( child.getAttribute(att) ) {
           this.getEBArecursive(list, child, att);
The recursive version passes the list of found nodes and the current node to itself. For each child of a node that's an element node (a tag), it checks for the attribute and records the node if it's found, otherwise, it call itself again on that child. When there's nothing left to search, the for loop does nothing and the recursion ends with a fully built list.

The other rather general function is getFieldDataFromRecord(). Given a node and a tag name, it digs into the node's subtree and extracts the content of that named tag:

     getFieldDataFromRecord : function (rec,field) {
       var found;
       for (var i=0; i < rec.childNodes.length; i++) {
         if (rec.childNodes.item(i).nodeName.
           toLowerCase()==field) {
           found = rec.childNodes.item(i).firstChild;
         if (found == null )
           return "";
           return found.nodeValue;
This function assumes that the records are arranged in a three-level hierarchy: the record's tag, each data item's tag, and the data held between start and end tags of the data item.

With that preparation out of the way, here's the meaty part.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date