| Trying not to get lost with a topic map | Table of contents | Indexes | Metadata deployment for publishing environments | |||
99 Tricks To Amaze Your Friends and Impress Your Boss with the DOM and XML in Web Browsers |
| Michael Leventhal |
| Architecture/Development |
| CiTEC
Silmukkatie 2 Vaasa Finland FIN-65101 Phone: +358 50 537 6091 Fax: +358 6 324 0800 Email: mle@citec.fi Web: www.doczilla.com |
Biographical notice: |
ABSTRACT: |
| application |
Three DOM applications are presented in this paper: a rapid document structure viewer, a document annotation tool, and a table which can be sorted on any column. The last example compares DOM and XSL (Extensible Stylesheet Language) approaches to this problem, arguing for the advantages of the DOM in terms of superior capability and comprehensibility of the code. |
Entirely new and postively thrilling demonstrations will be shown during the conference presentation. |
| browsers |
The presentation will be given at approximately the same time as browser vendors have either newly-released production releases or soon-to-be-released advanced beta code which supports the DOM and XML. This presentation will offer timely and accessible information of immediate value on a topic of broad general interest. |
Mea Culpa |
browser ![]() |
The primary objective of this paper and associated presentation is to show what you can do with the DOM in the new web browsers from Microsoft and the Mozilla-based browsers from Netscape and CiTEC. |
DOM, Document Object Model ![]() |
Although it is a rare enough occasion that the author has ever finished a paper before the deadline there was this time good cause for extreme procrastination. As of approximately two months before the XML Europe Conference none of the above browsers had a full and debugged implementation of the DOM . Since this is a practical presentation with working examples the job of the author was difficult. However, when the actual presentation is given at the end of April Microsoft will have released the final version of IE5 and the Mozilla browsers, which are today claiming a very high degree of DOM -compliance will be pretty well debugged. So there will be new and exciting stuff which is not in this paper at the presentation and an update on the bugs and compliance situation. |
| DocZilla |
While we are getting the mea culpas out of the way here let me add two more. First, the author is a member of development team that created CiTEC's DocZilla and the examples shown in this paper are known only to run under DocZilla. It is true that the DOM is a standard intended to enable browser-independent processing of structured documents but the scripting language in which the DOM is implemented will contain historical and other accidental variants. I promise to try all the scripts under all the browsers by the conference and report on the results. The reader will probably notice a general DocZilla slant for which I ask for the reader's comprehension that DocZilla happens to be what I know and love best. |
Just Enough DOM |
DOM, Document Object Model ![]() SGML ![]() |
Here goes my attempt to recite all the truth in the universe in the time that I can remain standing on one foot. |
| tree |
The term "tree" will be used often in this presentation to describe the representation of the document which is exposed by the DOM . In fact, every well-formed XML document does have a implicit tree representation but the DOM in the strictest sense never mandates any underlying structure of the data, only interfaces that make DOM operations look a lot like simply navigating through XML's document tree. The caution about what goes on under the covers really applies only to those brave souls implementing base DOM support as they should not assume a straight and simple tree implementation will produce the kind of performance required by many applications. The person writing DOM scripts in browsers will, on the other hand, find it quite useful to visualize the DOM document as a tree with elements and other XML objects as branches, with the branches decorated with attribute name/value pairs. The most common operation will be traversing the document through parent, child, and sibling relationships. If you can understand processChildren in the first application shown in this paper you will be well on you way to becoming a buff (expressions like this are occasionally put into this paper to help my European colleagues learn some American (Californian even!)) DOM script developer. |
JavaScript ![]() XPCOM XPCONNECT components ![]() |
In this paper we illustrate the use of the DOM through JavaScript programs. In fact the DOM can be accessed in the browser environment at several different levels and in several languages. The DOM standard itself is not only underlying data structure independent but also language-independent. Mozilla-based browsers implement the DOM as an internal interface in C++. This layer is accessible through the component interface of Mozilla, XPCOM, to other Mozilla components, to JavaScript programs through XPCONNECT, and to Java applets. But the JavaScript layer is probably the easiest and safest to start with and the correct level at which to program most applications. I will very likely show however other types of DOM applications during the presentation. |
| DHTML |
It should be noted that the DOM is not to limited to use in browsers; the interface it specifies is being employed in many applications ranging from operating systems to e-commerce middleware. There is, however, a historical relationship to DHTML (Dynamic HTML) and the browser environment in that one the original objectives of the DOM activity was to harmonize Netscape and Microsoft's non-interoperable implementations. Fortunately, at the same time as this need arose XML was embraced as a new Web standard by the W3C with the result that the DHTML problem was addressed by giving the DOM a solid architectural footing by basing its core on XML structured document concepts. |
DOM Level 2 DOM, Document Object Model ![]() events stylesheets |
Currently the DOM Level 1 is a W3C Recommendation and it is hoped that the browser vendors will be forthcoming with 100% clean and beautiful DOM implementations. A draft is out of the next revision the DOM , Level 2, but it is too early to state definitely what will finally be in it. Mozilla has, however, committed to including early implementations in two areas, browser events and access to stylesheets. The former is needed to provide standard mechanisms to replace the DHTML-related mostly non-interoperable event handling capabilities in JavaScript and to expand event handling into non-browser related applications. Access to the stylesheet is a hugely cool thing that will let you create and modify styles on the fly. In the applications shown in this paper events are handled in a pre- DOM level 2 fashion, although closer to the DOM way then the old and bad DHTML style. Dynamic stylesheets are presented through tricks involving multiple stylesheets and jiggling hide and show display properties. It is pretty clever but scripts will be much less clever and much better when the DOM lets you do this stuff the right way. These not-yet-really-standard-based improvements will be shown in applications to be unveiled at the conference. |
| REC-DOM-Level-1 |
Of course, the place to get definitive answers on what the DOM does and does not do is the W3C Recommendation itself at http://www.w3.org/TR/REC-DOM-Level-1 . I quite honestly believe that this is a very good specification, short, sweet, clear, always erring on the side of saying too little rather than overspecifying, admitting its imperfections from the start and teaching us how to cope with them. The Recommendation includes the JavaScript language bindings, an appendix which will become your dearest friend if you are going to start writing DOM applications. You'll find that you can pretty much toss out that nice JavaScript book you bought 4 weeks ago. |
OK, so much for all the truth in the universe. Now on to some real stuff! |
View Branch Demo |
View Branch Demo - Functionality |
| tree view |
The View Branch script displays the name of the element which the cursor is currently over, its attributes, and each of its ancestors up to the document root. This information is displayed in the status bar of the browser and is automatically refreshed whenever the cursor is moved over another element. In DocZilla the operation is very fast; the branch information is written nearly instantaneously and the user does not perceive any slowness in the operation of the browser. The application works on any XML, SGML, or HTML file. |
|
PARA[]<-LISTITEM<-ITEMIZEDLIST<-SECT2<-SECT1<-PREFACE<-BOOK |
which is telling us that our document's markup structure looks like this: |
<BOOK> <PREFACE> <SECT1> <SECT2> <ITEMIZEDLIST> <LISTITEM> <PARA>Display of XML, SGML and HTML documents with CSS.</PARA> </LISTITEM> </ITEMIZEDLIST> </SECT2> </SECT1> </PREFACE> </BOOK> |
| contextual editing structured search stylesheet editing |
Many concrete applications can be imagined for this application - for example, contextual editing, guiding structured search, exposing "hidden" information in element names and attribute values. It has been extremely handy in creating CSS stylesheets for existing XML and SGML documents. |
| mouseover event |
The DOM is used, of course, to get the name and attributes of the current element and to obtain the name of each ancestor, using the DOM structure which links a child element to its parent. A mouseover event assigning a handler (a function which obtains and displays the structure information when the mouse passes over the element) is set for each element in the document when the document is loaded. This requires us to traverse the entire document tree through the DOM . |
View Branch Demo - Code |
The "main routine" has just a single line initiating the assignment of an event handler to each element in the document. |
setTimeout(setUpEvents,0); |
setUpEvents assigns the function wstatus as the onmouseover event handler on the document element and invokes processChildren to do the same on the entire document tree. |
function setUpEvents() {
document.documentElement.onmouseover = wstatus;
processChildren(document.documentElement);
}
|
| recursion |
processChildren is a simple recursive procedure for traversing the document tree. |
function processChildren(node) {
var currentNode = node.firstChild;
while (currentNode != node.lastChild && currentNode != null) {
currentNode.onmouseover = wstatus;
if (currentNode.hasChildNodes) {
processChildren(currentNode);
}
currentNode = currentNode.nextSibling;
}
if (node.lastChild != null) {
currentNode.onmouseover = wstatus;
if (node.lastChild.hasChildNodes) {
processChildren(node.lastChild);
}
}
}
|
wstatus is the event hander which obtains and prints branch information on the actual mouseover event. It is is passed the element 'e' on which the mouseover event occured. |
function wstatus(e) {
|
| bubbling |
If cancelBubble is not set to true the event will "percolate" up to the element's ancestors (which will be treated as the current element). |
e.cancelBubble = true; |
| pseudo-element |
The event actually occurs on a text pseudo-element (specified by the DOM ) so to get the GI of the current element we actually need to get the parent. |
var pnode = e.target.parentNode; wintext = pnode.nodeName; |
Here we obtain the attribute information for the current element. |
if (pnode.attributes != null) {
wintext = wintext + " [";
for (var i=0; i<pnode.attributes.length; i++) {
var currAttr = pnode.attributes[i];
wintext = wintext + " " + currAttr.nodeName + "=" + currAttr.nodeValue;
}
wintext = wintext + " ]";
}
|
And here we will go up the branch adding each parent until we have no more parents. |
pnode = pnode.parentNode;
while (pnode != null) {
wintext = wintext + "<-" + pnode.nodeName;
pnode = pnode.parentNode;
}
window.status = wintext;
}
|
Annotation Demo |
Annotation Demo - Functionality |
| annotation |
This demo shows the annotation of a document in the browser through the DOM . |
| French |
In the figure below you will see an SGML document displayed in the right-hand frame "Selections from French Poetry". Clicking on an element (which we do when we click anywhere in the right frame) causes the name of that element to be written in the HTML form document in left frame in the small field to the right of the text "Source Element:" and the textual content of the element to be written in the big textarea below. This confirms to the user what element he or she is actually annotating, showing the "extent" of the element (element content is not shown in the branch view on the status bar), and also provides the text of the element to assist in formulating the annotation (whether to copy part of the text into the Annotation Text area or simply to more easily refer to it visually). We may then enter an annotation in the textarea below the text "Enter Annotation Text". Before adding the annotation to the SGML document in the right-hand frame by clicking on the "Attach Annotation" button we select one or more of the checkboxes to indicate the type of the annotation (note, hint, or translation). When the annotation has been attached it will appear in the right-hand frame below the element it annotates surrounded by a frame color-coded by annotation type. The annotation type checkboxes also act as hide/show toggles for existing annotations: when the checkbox of an annotation type is selected all annotations of that type are shown and if the checkbox is not selected all annotations of that type are hidden. |
|
In the example shown above, we have clicked on the line "Comme il pleut sur la ville". We see in the left hand side |
Source Element:LIGNE Comme il pleut sur l a ville |
TRAN[]<-STROPHE<-POEME<-SECTION<-POESIE |
Because the annotation is truly a part of the document tree we can even annotate the annotation by clicking on it. |
| language learning |
For this demo application we imagined that it could be useful to hide some types of annotations in certain situations. For example, suppose this is being used by students learning French. They might wish to view only Hints as they are trying to understand the French text and only look at the Translation when they wish to verify their understanding of the poem. The demo provides this functionality. Let us create a hint for "Comme il pleut sur la ville". We click again on the line of poetry, type "Bring your umbrella" in the "Enter Annotation Text" textarea, click on the "Hint" checkbox, uncheck the "Translation" checkbox, and hit the "Attach Annotation" button. We will have seen the red translation annotation disappear and a green hint annotation appear with our text. If we wish to see the translation appear again we simply check the translation checkbox again and our translation reappears. |
| disable stylesheet |
The functionality is achieved through access to the disable property of the cascading stylesheet documents. We actually have 4 stylesheets, one base and three cascades which simply override the display property of annotation elements. Each time we wish to hide or show one of the annotation elements we simply enable or disable its cascading stylesheet that controls whether or not it is displayed. This operation can be done even more efficiently with DOM Level 2 operations which can directly manipulate the stylesheet to switch between display: hide and display: show properties of the annotation elements. |
Annotation Demo - Code |
The code has been trimmed down to just show manipulations of the note annotation type. Some cloned code dealing with translation and hint types has been excised. |
The process for setting up events on individual elements using setUpEvents and processChildren is the same as used for the Branch View application. |
var notes = new Array();
var annNode;
var noteNode;
var textNode;
var noteCount = 0;
var noteLast;
setTimeout(setUpEvents,0);
function setUpEvents() {
var there = parent.frames.poesie.document;
|
| getElementsByTagName |
The next little bit is grungy. When this application was written it was not actually possible to create new DOM nodes as the code for this DOM operation had not been completed. So we fake it by appending nodes to the end of the SGML document which we be repositioned in the DOM tree when an annotation is actually "created" by the user. The user does not see those notes at the end because the are hidden by the stylesheets. This all works o.k. except that we have to copy all our note elements to a new array as the one returned by the getElementsByTagName DOM operation is read-only. |
noteSet = there.getElementsByTagName("note");
noteLast = noteSet.length;
for (var i=0; i<noteLast; i++) {
notes[i] = noteSet[i];
}
there.documentElement.onmousedown = fillsource;
processChildren(there.documentElement);
}
function processChildren(node) {
var currentNode = node.firstChild;
while (currentNode != node.lastChild && currentNode != null) {
currentNode.onmousedown = fillsource;
if (currentNode.hasChildNodes) {
processChildren(currentNode);
}
currentNode = currentNode.nextSibling;
}
if (node.lastChild != null) {
currentNode.onmousedown = fillsource;
if (node.lastChild.hasChildNodes) {
processChildren(node.lastChild);
}
}
}
|
function fillsource(e) {
e.cancelBubble = true;
annNode = e.target.parentNode;
document.form1.ae.value = annNode.nodeName;
var input = new String(e.target.nodeValue);
minput = input.replace(/\\s/g," ");
var output = new String(");
iterations = Math.floor(minput.length / 20) + 1;
for (i=0; i<iterations; i++) {
start = i*20;
end = (i+1)*20;
output = output + minput.slice(start,end) + '\\r\ ';
}
document.form1.source.nodeValue = output;
document.form1.annot.nodeValue = ";
}
|
function attach() {
var typeNode;
if (document.form1.atype.parentNode.childNodes[0].checked) {
typeNode = notes[noteCount];
noteCount++;
typeNode.firstChild.nodeValue = document.form1.annot.nodeValue;
if (annNode.nextSibling != null) {
annNode.parentNode.insertBefore(typeNode,annNode.nextSibling);
}
else {
annNode.parentNode.appendChild(typeNode);
}
}
|
Here we toggle the display of the note annotation. showNote is invoked twice to ensure that the stylesheet that hides notes is turned off and the base stylesheet which shows notes is in effect. |
if (document.form1.atype.parentNode.childNodes[0].checked) {
showNote();
showNote();
}
}
function showNote() {
setTimeout(toggleNoteStyleSheet, 0);
}
var noteEnabled = true;
function toggleNoteStyleSheet()
{
if (noteEnabled) {
parent.frames.poesie.document.styleSheets[1].disabled = true;
}
else {
parent.frames.poesie.document.styleSheets[1].disabled = false;
}
noteEnabled = !noteEnabled;
}
|
These are some empty notes added to the end of our SGML document - a kludge that is not necessary in most recent versions of DocZilla but perhaps a good trick to remember anyway. |
<templates> <note>.</note> <note>.</note> |
The empty note nodes are not displayed to the user because they are enclosed in a templates element with a display property set to none. |
templates
{
display: none;
}
|
This is from the base stylesheet which sets the formatting of the notes. |
note
{
display: block;
margin-left: 40px;
font-size: 10pt;
border: 2px solid blue;
}
|
And this is the entire contents of the stylesheet we enable when we want to hide notes. |
note
{
display: none;
}
|
Sortable Table Demo (Stock Index) |
DOM, Document Object Model ![]() XSL ![]() interactive transformation ![]() |
In this section we will compare the effectiveness of XSL to the DOM for transforming XML documents and scripting dynamic, interactive behavior. |
sorting ![]() |
The concept for the application and the XSL code are from Microsoft XML demo material. In fact, the original objective, very successful achieved I might add, was to show that "anything XSL can do the DOM can do better". However, the primary objective of this paper is to show how to use the DOM so there will only be a little explanation of what is going on in the XSL . The DOM application is a bit different, better in fact as it supports attribute-controlled sorting algorithm selection and reads and displays the system time. |
CSS, Cascading Style Sheets ![]() XML glitterati XSL ![]() |
Although such statements have done little to increase my popularity among the XML glitterati XSL does not add one iota of capability over what can be accomplished right now with the DOM and CSS . And XSL is a year or two away and the DOM and CSS are here now. OK, I've got that off my chest. Now see for yourself. |
XSL Stock Sorter - Functionality |
| stock table |
The rows of the table shown in the figure below can be sorted based on values in any individual column by clicking on the header of the column. The sort is a straight case-sensitive (z comes before A) string sort and it does not handle numeric or other types of values. |
|
The file downloaded to the browser is XML. A snippet is shown below: |
<?xml:stylesheet type="text/xsl" href="portfolio.xsl"?> <portfolio> <date>October 13, 1998 at 3:56PM</date> <stock> <symbol>ACXM</symbol> <name>acxiom corp</name> <price>$18.875</price> <change>-1.250</change> <percent>-6.21%</percent> <volume>0.23M</volume> </stock> |
| HTML conversion |
XSL is used to convert the XML into an HTML table. Each table header has a nested Div tag with an onClick event to invoke the XSL sort, passing the name of the field to be sorted to the routine. The entire document is regenerated in HTML each time the table is sorted. |
XSL Stock Sorter - Code |
The transformation of the XML data shown above to an HTML table is defined here. Scripts for sorting on a specific column field and for sorting on document load are also embedded here. |
<?xml version="1.0"?>
<HTML xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<HEAD>
<STYLE>
BODY {margin:0}
.bg {font:8pt Verdana; background-color:purple; color:white}
H1 {font:bold 14pt Verdana; width:100%; margin-top:1em}
.row {font:8pt Verdana; border-bottom:1px solid #CC88CC}
.header {font:bold 9pt Verdana; cursor:hand;
padding:2px; border:2px outset gray}
</STYLE>
<XML id="portfolio">
<xsl:apply-templates select="portfolio">
<xsl:template>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:apply-templates>
</XML>
<XML id="sorted"><xsl:eval/></XML>
<XML id="sortStocks" src="sort-stocks.xsl"><xsl:eval/></XML>
</HEAD>
<SCRIPT><xsl:comment><![CDATA[
function sort(field)
{
sortField.value=field;
sorted.XMLDocument.loadXML(
portfolio.transformNode(sortStocks.XMLDocument));
}
]]></xsl:comment></SCRIPT>
<SCRIPT for="window" event="onload"><xsl:comment><![CDATA[
sortField = sortStocks.selectSingleNode("//@order-by");
sort("price");
]]></xsl:comment></SCRIPT>
<BODY>
<TABLE width="100%" cellspacing="0">
<TR>
<TD class="bg"/>
<TD class="bg">
<H1>Stock Index for <xsl:value-of select="portfolio/date"/></H1>
</TD>
</TR>
<TR>
<TD class="bg" width="120" valign="top">
<P>Click on the column headings to sort by that field.</P>
<P>Note that sorting is by string value only and not numeric</P>
</TD>
<TD valign="top">
<TABLE class="listing" datasrc="#sorted">
<THEAD>
<TD width="200">
<DIV class="header" onClick="sort('name')">Company</DIV>
</TD>
<TD width="80">
<DIV class="header" onClick="sort('symbol')">Symbol</DIV>
</TD>
<TD width="80">
<DIV class="header" onClick="sort('price')">Price</DIV>
</TD>
<TD width="80">
<DIV class="header" onClick="sort('change')">Change</DIV>
</TD>
<TD width="80">
<DIV class="header" onClick="sort('percent')">%Change</DIV>
</TD>
</THEAD>
<TR>
<TD>
<DIV class="row" datafld="name"><xsl:eval/></DIV>
</TD>
<TD>
<DIV class="row" datafld="symbol"><xsl:eval/></DIV>
</TD>
<TD>
<DIV class="row" datafld="price" STYLE="text-align:right">
<xsl:eval/>
</DIV>
</TD>
<TD>
<DIV class="row" datafld="change" STYLE="text-align:right">
<xsl:eval/>
</DIV>
</TD>
<TD>
<DIV class="row" datafld="percent" STYLE="text-align:right">
<xsl:eval/>
</DIV>
</TD>
</TR>
</TABLE>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
|
The contents of sort-stocks.xsl, referenced in the script above, is shown below. It triggers the transformation of the document, I think. |
<xsl:template xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:apply-templates select="portfolio"> <xsl:template> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="portfolio"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:apply-templates select="stock" order-by="name"/> </xsl:copy> </xsl:template> </xsl:apply-templates> </xsl:template> |
DOM Stock Sorter - Functionality |
sorting ![]() |
The functionality is similar to the XSL Stock Sorter - but with some important differences. Type-specific and directional sorting has been implemented, the default values are actually encoded in the column header (One could imagine being able to change properties of the sort from controls in the page.) There is an XML column header instead of the column headings hard-coded in the script found in the XSL application. Here is a snippet of data showing the header and a row of stock information: |
<?xml version="1.0"?> <?xml-stylesheet href="stuff.css" type="text/css"?> <portfolio> <stockhead> <headrow> <colhead field="string/g" direct="asc">Symbol</colhead> <colhead field="string/g" direct="asc">Name</colhead> <colhead field="curr" direct="desc">Price</colhead> <colhead field="num" direct="desc">Change</colhead> <colhead field="units" direct="desc">%Change</colhead> <colhead field="units" direct="desc">Volume</colhead> </headrow> </stockhead> <stockbody> <stock> <symbol>ACXM</symbol> <name>Acxiom Corp</name> <price>$18.875</price> <change>-1.250</change> <percent>-6.21%</percent> <volume>0.23M</volume> </stock> |
| CSS2 table |
The table is formatted in XML using CSS 2 table display properties. It looks quite good, I daresay: |
|
The stylesheet for this application is shown below: |
portfolio
{
display: table;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 0.5em;
}
symbol, name, price, change, percent, volume
{
display: table-cell;
font-family: Arial, Helvetica, sans-serif;
font-size: x-small;
line-height: 1.2;
vertical-align: top;
padding-left: 0.2em;
padding-right: 0.1em;
padding-top: 0.1em;
border-bottom: 1px solid navy;
}
colhead
{
cursor: pointer;
display: table-cell;
font-family: Arial, Helvetica, sans-serif;
font-weight: bolder;
font-size: small;
text-align: center;
line-height: 1.2;
vertical-align: top;
background: #CCC;
color: black;
padding-left: 0.2em;
padding-right: 0.2em;
padding-bottom: 0.05em;
border: 2px inset black;
}
stockhead stock symbol,
stockhead stock name
{
text-align: left;
}
change,
price,
percent,
volume
{
text-align:right;
padding-right: 0.4em;
}
stock, headrow
{
display: table-row;
}
stockhead
{
display: table-header-group;
}
stockbody
{
display: table-row-group;
}
|
| code |
Of course, we have no need to regenerate the entire document. Another very important point about the DOM approach is that the code is all totally generic - it will work on any table having matrix (no spanned rows or colums) structure and headers. |
DOM Stock Sorter - Code |
var gHeaderRowCellGI = "COLHEAD"; var gRowCellGI = "STOCK"; var gSortDirectionAttributeName = "DIRECT"; var gFieldTypeAttributeName = "FIELD"; var gTableCells = new Array(); makeSortableTable(gRowCellGI); |
architectural forms ![]() |
It may be of interesting to note that this information could be passed to the application by means of architectural forms, a technique by which any element or attribute can be associated with an abstract element type such as COLHEAD, STOCK or attribute type such as DIRECT or FIELD and thereby entirely avoiding any markup-specific code. (If, of course, the browser has the ability to understand architectural forms. DocZilla will probably have that property in its first release.) |
function makeSortableTable(rowCellGI)
{
|
We will set up sort event handlers on the cell headers. The technique is the same employed in the two previous DOM application examples. |
setTimeout(SetSortOnCellHeaderEvents,0); |
We get all the row elements and store them in global array. Then we call putDOMNodeIntoSortTable to extract the cell elements in the rows and store them in a two dimensional array ready for sorting. |
var celllist = document.getElementsByTagName(rowCellGI);
for (var i=0; i < celllist.length; i++) {
gTableCells[i] = celllist[i];
}
putDOMNodesIntoSortTable(gTableCells);
}
function putDOMNodesIntoSortTable(nodes)
{
var i, j;
var ncount = nodes.length;
for (i = 0; i < ncount; i++) {
var node = nodes[i];
var childNodes = node.childNodes;
var ccount = childNodes.length;
for (j = 0; j < ccount; j++) {
var child = childNodes[j];
node[j] = child.firstChild.nodeValue;
}
}
}
|
The technique used to set events on column headers in SetSortOnCellHeaderEvents should be well understood by now from the prior DOM examples. |
function SetSortOnCellHeaderEvents() {
var headerCells = document.getElementsByTagName(gHeaderRowCellGI);
for(var i=0; i<headerCells.length;i++) {
headerCells[i].onmousedown = sortOnColumn;
}
}
|
sorting ![]() |
sortOnColumn is the event handler invoked when the user clicks on a header cell. First it must get the fieldtype and direction attributes from the column header to invoke the proper sorting for the column. Actually we just iterate over the column header cells until we find a node which matches the node on which the event was triggered. Once that node is found all we need to do is invoke the low level sort routine, passing the column number, fieldtype, and direction as arguments. |
function sortOnColumn(e) {
// node is text, parent is headercell, parentparent is
// container for header cols
var node = e.target.parentNode.parentNode.firstChild;
for (i=0; i<e.target.parentNode.parentNode.childNodes.length; i++)
{
if (node == e.target.parentNode) {
var fieldtype = ";
var direction = ";
for (var j=0; j<node.attributes.length; j++) {
var currAttr = node.attributes[j];
if (currAttr.nodeName == gFieldTypeAttributeName) {
fieldtype = currAttr.nodeValue;
}
else if (currAttr.nodeName == gSortDirectionAttributeName) {
direction = currAttr.nodeValue;
}
}
sort(gTableCells,i,fieldtype,direction);
break;
}
else {
node = node.nextSibling;
}
}
}
|
sorting ![]() |
This is a really bad sort routine so I should be careful here to blame the folks I stole it from: it was lifted from one of Netscape's demos. I added the nice bits with handling the fieldtypes and direction. The sort is just a bubble sort, the more unsorted the table is the more swapping of nodes takes place. Comparison between nodes is done with compare function which takes into account the fieldtype and direction. |
function sort(collection, key, fieldtype, direction)
{
var i, j;
var count = collection.length;
var parent, child;
for (i = count-1; i <= 0; i--) {
for (j = 1; j <= i; j++) {
if (compare(collection[j-1][key], collection[j][key],
fieldtype, direction)) {
// Move the item both in the local array and
// in the tree
child = collection[j];
parent = child.parentNode;
collection[j] = collection[j-1];
collection[j-1] = child;
parent.removeChild(child);
parent.insertBefore(child, collection[j]);
}
}
}
}
|
JavaScript ![]() |
compare makes use of JavaScript's decent regular expression, string, and math classes, quite necessary even in a relatively small application such as this one. |
function compare(value1, value2, fieldtype, direction) {
var str;
if (fieldtype.toUpperCase() == "STRING/G") {
value1.toUpperCase();
value2.toUpperCase();
}
else if (fieldtype.toUpperCase() == "CURR") {
var str = value1.match(/\\$\\s*(\\d*\\.\\d*)/);
value1 = RegExp.$1 * 1.000;
var str = value2.match(/\\$\\s*(\\d*\\.\\d*)/);
value2 = RegExp.$1 * 1.000;
}
else if (fieldtype.toUpperCase() == "NUM") {
value1 = value1 * 1.000;
value2 = value2 * 1.000;
value1 = Math.abs(value1);
value2 = Math.abs(value2);
}
else if (fieldtype.toUpperCase() == "UNITS") {
var str = value1.match(/((+|-)?\\d*\\.\\d*)/);
value1 = RegExp.$1 * 1.000;
var str = value2.match(/((+|-)?\\d*\\.\\d*)/);
value2 = RegExp.$1 * 1.000;
value1 = Math.abs(value1);
value2 = Math.abs(value2);
}
if (direction.toUpperCase() == "DESC") {
return (value1 < value2);
}
else {
return (value1 > value2);
}
}
|
Conclusion |
DOM, Document Object Model ![]() |
You can do absolutely anything with XML, CSS , the DOM and a scripting language in Web browsers that fully support those four things. Period. The promised land is here. What are you waiting for??!! |
| Trying not to get lost with a topic map | Table of contents | Indexes | Metadata deployment for publishing environments | |||