One handy feature of Internet Explorer is the ability to use data islands to link data to html controls on a page. This feature is not readily built into Mozilla, but one can easily mimic this behaviour to build cross-browser web applications.
The basic data island we are going to use is a simple xml element either linked to the page or explicitly coding the xml into the page. For instance, let us illustrate a simple example:
<xml id="xmlDataIsland"> <loaninfo> <borrower id="1"> <firstname>Brian</firstname> <middlename>Ellis</middlename> <lastname>Johnson</lastname> </borrower> </loaninfo> </xml>
With this data island, we can populate any number of controls on a page simply by linking the data island to the controls via some JavaScript and the DOM.
To link this, all we need to do is have a function to handle populating the controls such as:
function loadFields() { var xmlNode = window.document.getElementsByTagName('xml'); var borrowerNode = xmlNode[0].childNodes[1]; if(borrowerNode.hasChildNodes()) { var i = 0; var xmlChildNode, nodeVal=; while(borrowerNode.childNodes[i]) { // get node xmlChildNode = borrowerNode.childNodes[i]; // check nodetype if(xmlChildNode.nodeType != 3) // #text == 3 { // get nodeValue (aka text) nodeVal += xmlChildNode.firstChild.nodeValue + ' '; } i++; } // set control value to nodeValue window.document.getElementById('txtBorrowerName').value = nodeVal; } }
Here is another example of an XML DataIsland for use in Mozilla or IE:
<xml id="mxxdataisland"> <mxxdataisland> <rowset tagid="DATES"></rowset> <rowset tagid="SUBJPRP"></rowset> <rowset tagid="PRODUCT"></rowset> </mxxdataisland> </xml>
This dataisland's purpose is to inform the Application Server what tables this page is going to need access to or be requesting information from.
The controls on the page are then linked by way of custom attributes MXXOBJECT and MXXPROPERTY, much like the DATASRC and DATAFLD attributes used by IE. This allows the XML data returned to be parsed and linked or "bound" to the controls.
Note: MXXOBJECT and MXXPROPERTY are just attributes that I made up, these actually could be any attribute.
Binding the controls:
<input type="text" id="PropertyStAddr1" name="PropertyStAddr1" style="height:21px;width:302px;text-align:left;" maxlength="35" class="fldDefault" mxxobject="SUBJPRP" mxxproperty="PropertyStAddr1" <-- here are our "binding" tags > <input type="text" class="fldZipCode" name="PropertyZip" id="PropertyZip" size="10" style="height:21px;width:69px;" mxxobject="SUBJPRP" mxxproperty="PropertyZip" <-- here are our "binding" tags mxxxmlnode="xmldef_PropertyZip" <-- this links to a control-level data island mxxtype="MXXZipCodeAutoLoadEdit" <-- optional custom type for control handling >
Since we are passing XML to the server, we can also send the server some additional info that a particular control may need, or alert the server of other controls on the page related to or driven by a control. The following is an example of a custom dataisland for a specific control type:
<select id="PropertyState" name="PropertyState" style="height:20px;width:48px;" class="cmbDefault" mxxtype="GFXStateList" mxxxmlnode="xmldef_PropertyState" mxxobject="GOXSUBJPRP" mxxproperty="PropertyState" > </select> <div style="width:0px; height:0px; visibility:hidden;z-index:1"> <xml id="xmldef_PropertyState"> <mxxstatelist> <status value="initialize"></status> <contenttype value="abbrev"></contenttype> <controls> <control type="countylist" tagid="PropertyCounty" contenttype="name" valuetype ="name"></control> </controls> </mxxstatelist> </xml> </div>
These XMLDataIslands don't do us any good just like this. In order for them to work we need to do 2 things.
1. Build control handlers to handle the updating and rendering of different control types.
(Note: Control types can be anything. Form objects, tables, spans, divs, iFrames, anything that you can access via the DOM and an ID is eligible)
2. Build a handler to build a dom to send to the server and parse the return back out to the controls.
All a control handler has to do is be able to updata a control value. This means all Text Inputs can share a handler, selects another, and so on. One way to do this is to scrape the controls on a given page into an associative array. Then, when a response is returned, we can parse out the xml via an id attribute to match up the xml payload with a control.
<input type="text" id="FirstName" ...>
Sample Object collector function:
// grab all the elements for parsing var tags = window.document.body.getElementsByTagName("*"); var pPrevElem = null; var pNextElem = null; for (var i = 0; i < tags.length; i++) { pHTMLElement = tags[i]; switch (pHTMLElement.tagName.toLowerCase()) { case "span": case "table": // this indexes by controlID and stores the elementObject m_MXXPageObjectsArray[pHTMLElement.id] = pHTMLElement; break; case "input": case "select": case "textarea": case "button": // this indexes by controlID and stores the elementObject m_MXXPageObjectsArray[pHTMLElement.id] = pHTMLElement; break; case "div": // this indexes by controlID and stores the elementObject m_MXXPageObjectsArray[pHTMLElement.id] = pHTMLElement; break; } }
The xml payload going out might be:
<page id="NewUser"> <formcontrols> <control id="FirstName"> <value /> </control> </formcontrols> </page>
The return would then be:
<page id="NewUser"> <formcontrols> <control id="FirstName"> <value>Dennis</value> </control> </formcontrols> </page>
The parsing handler would then take the response, load it into an XML DOM, then pass out each node to the appropriate control handler.
processTextControl(control, xmlNode);
Sample parsing of returned xml:
// parseout to controls var formControlNodes = xmlDoc.getElementsByTagName('formcontrols'); for(i=0; i<formControlNodes.length;++i) { var pFormControlNode = formControlNodes[i]; var pPageObject = m_MXXPageObjectsArray[pFormControlNode.getAttribute('id')]; if(!pPageObject) continue; processTextControl(pPageObject, pBoundControlNode); }
The control handler would then rip out the necessary data to populate the control. In this case the value nodevalue would be used to set control.value. A select could have the options to loop through and create new Options() with or even just replace the node or innerHTML with the new payload.
Finally here is a small table sample using XML Data Islands that works in both IE6 and Mozilla.
For any of the above to do us any good, we need to get this info to the server. We can do this a number of ways, including ASP, JSP, and CGI. I'm going to use XMLHTTP since it allows for me to update a page without having to reload it and it allows for the controls to update other controls and act like a regular 2 tier applicationa by providing instant updates and event handling.
Author: Thad Hoffman
Original Document Information
- Author(s): Thad Hoffman ([email protected], [email protected])
- Other Contributors: Heikki Toivonen ([email protected])
- Last Updated Date: June 21, 2005
- Copyright Information: Copyright (C) Thad Hoffman, Heikki Toivonen