Why XSLT is Great for AJAX Components: The XSLDataGrid

Author: Lindsey Simon <lsimon@commoner.com>

Contents

Abstract

Most web applications have a requirement somewhere in their interface for a tabular view of data - oftentimes a view of the rows in a database table. In some cases, the use of a static HTML <TABLE> is appropriate, but users have become increasingly accustomed to richer more malleable interfaces that let them change column widths, order, etc... Among the application widgets in a developer's toolbox, the dynamic datagrid seems like the most commonly cumbersome one to set up. This paper will outline a technique to instantiate a datagrid by using semantic markup followed by an XSL transformation; achieving ease of setup along with high performance and minimum dependence.

The Problem

There are roughly three types of approaches to using a Javascript widget in a web application. Approach #1 involves creating a server-side object with properties (in PHP or Ruby, for instance), and then eventually calling some sort of render method on the object that creates a XHTML string to be sent to the browser. For instance, in PHP it might look something like this:

$dg = new DataGrid();
$dg->columns = array( "field1", "field2", "field3" );
$dg->data = $data;
etc...
$dg->render();
Approach #2 is much like approach #1, except that an object is instantiated in the browser (with Javascript) and the render method is essentially a series of DOM (Document Object Model) object creations and attachments to the browser's render tree. For example, to use the ActiveWidgets Grid the code might look like this:
var myCells = [
["MSFT","Microsoft Corporation", "314,571.156"],
["ORCL", "Oracle Corporation", "62,615.266"]
];

var myHeaders = ["Ticker", "Company Name", "Market Cap."];

// create grid object
var obj = new AW.UI.Grid;

// assign cells and headers text
obj.setCellText(myCells);
obj.setHeaderText(myHeaders);

// set number of columns/rows
obj.setColumnCount(3);
obj.setRowCount(2);

// write grid to the page
document.write(obj);
Approach #3 is a combination of approaches #1 and #2, whereby some relatively simple, declarative XHTML is used as a starting point, and some method is used to populate the render tree with extra bells and whistles; an analogy here is of building a house where the declarative xhtml is the frame and the DOM additions are the siding, air conditioning, and champagne-filled hot tub ;)

var xdg1 = new XSLDataGrid( 'renderDiv', { width: 480, height: 200, transformer:'client', debugging: true } );

Approach #1 to widget creation is the least portable as it relies on whatever server-side language a developer is using at the time. Approach #2 will most likely degrade poorly if an end-user has disabled Javascript in their browser; this approach also tends to be less performant with larger datasets. Thus, compromise, you mother of endless tinkering, will be our guide to Approach #3.

In Defense of the Table Tag

The advent of more and better CSS support (Cascading Style Sheets) in browsers has caused developers to reconsider their use of the <TABLE> tag. For the most part this is a great thing, as tables can be constraining and inefficient for design and layout. However, the <TABLE> tag and its children offer a declarative, semantic language for describing the presentation of tabular data that succeeds where an obscure combination of nested DIVs and float:left's becomes cumbersome. And if a user agent has CSS disabled, the pure DIV approach to table presentation will be a mess.

XSLT On XHTML [1]

XSLT offers developers a powerful, flexible, and well-formed-markup-ensuring mechanism for decorating the DOM. XSL transforms for UI widgets need to be followed by a call to put the resultant XHTML back into the DOM being rendered. The XSLDataGrid uses its container's innerHTML property to do so (this is a technique known as AHAH [2]). It is worth noting here that many argue against using the proprietary, but well-supported, innerHTML property to populate the DOM. Regarding tables specifically, in an article titled "Javascript Benchmark - W3C DOM vs. innerHTML" the ever-invalueable quirksmode.org has this to say:

"The most obvious conclusion of these tests is that innerHTML is faster than "real" W3C DOM methods in all browsers. The W3C DOM table methods are slow to very slow, especially in Explorer.

More than once people said that creating elements only once and then cloning them when necessary leads to a dramatic performance improvement. These tests don't show anything of that kind. Although in most browsers cloning is minimally faster than creating, the difference between the two methods is small."

So what does the picture look like with XSLT for widget instantiation? The following diagram shows how the XSLDataGrid works.

Semantic Table
Semantic XHTML Table
+ XSLDataGrid.xsl = Decorated XHTML
More tags, attributes,
CSS, etc ...
+ Javascript
Instantiation,
event listeners,
etc ...
= Semantic Table
Rich DataGrid UI in Render Tree
and XML DOM in Dual-DOM in Memory

Dual-DOM or How I Dealt with innerHTML

There are essentially three ways to use the XSLDataGrid component - either 1) by fetching the transformed, fully decorated XHTML each time from the server, 2) by fetching semantic XHTML from the server and transforming it on the client, or 3) transforming xhtml already on the page in the client. In case 1, we don't really have to worry about keeping up with the DOM in the client since we can farm off all the change operations(column resize, sort, and reorder) to the server, re-filling the container's innerHTML each time. Cases 2 and 3 are different, because we want to perform XSLT multiple times - in other words, we will continue to need some valid XHTML we can perform XSLT on. It's important to note that with innerHTML, what you put in is not necessarily what you get out when you read it. Internet Explorer, for instance, does some major "optimization" to the HTML, removing quotation marks, end tags, etc... Because we cannot quickly and reliably get well-formed XHTML from the container's innerHTML, when the XSLDataGrid initializes, it saves an XML DOM Document made from the original semantic XHTML in memory(aka Dual-DOM). It is also usually easier and more efficient to update this simpler XML DOM, when we want to perform change operations, than it would be to update the more complex, decorated DOM in the render tree. Let's take the example of resizing a column. In order to update the DOM in the render tree, it would take updating a great number of the DIV's, SPAN's, TH's, TD's, COL's etc..., all the while taxing our client to render these changes as we make them. In the XML DOM, all we change is one width attribute's value and then re-run the XSLT. This approach allows us to do all of the "heavy lifting" in our XSLT engine; get the presentation rules right once in XSL and then leverage it.

XSLT in the Browser

Internet Explorer and Firefox both offer exposed APIs for XSLT and Manos Batsis has released a free software Javascript library named Sarissa which wraps up the whole process of loading the XHTML, the XSL, and then calling the browser's native transform method. At the time of this writing, it appears that XSLT is not exposed to Javascript in Safari or Konqueror. Support for Opera's XSLT API is not yet implemented in the XSLDataGrid.

Client side sorting is done in the XSLDataGrid by first using DOM to strip out a subset of template nodes from the original xsl file. Then, the current TBODY content is transformed using this subset of then nodes along with a few param set's and voila! Unfortunately, this sorting technique is limited to the data-types as known to XSLT 1.0, which is only "text" and "number". "date" would be pretty useful, and I suspect that over the next weeks I will work on additional qname stylesheet templates to handle other sort cases.

Rows Pre-XSLT (kilobytes) Post-XSLT (kilobytes) Client-side XSLT Firefox (ms.) Client-side XSLT IE6 (ms.) Server-side XSLT (ms.)
200 17.5 29.6 27 7 30.6
500 43.6 68.8 63 12 35.6
1000 87.1 134 119.6 17 86
2000 179.1 270.5 264 38 206.8
4000 363.1 543.5 512 85 397.9
8000 731.1 1089.5 1074 167 786.1
20000 1885.1 2787.5 2573 375 2322
Graph - XSLT in client

Conclusions

The greatest advantage to using XSLT for a Javascript widget is the flexibility it provides for instantiation. Most AJAX-using web developers are going to be working with a server-side component/language, and having the option to reduce a clientside Javascript decoration step to improve performance is nice, though it comes with a bandwidth price. In many projects, developers may be faced with a mixed bag - they may have a need for some large dynamic datagrids which can only be originated on the server as well as some smaller hand-coded tables, where a less-rich datagrid would be fine. For instance, a developer might not always want to capture the fact that user changed a column's size and store it as a preference, but even for these less-rich datagrids, they do want them to look and feel the same. The XSLT approach gives the developer an opportunity to choose either a client or server based technique to achieve a similar result.

Footnotes

[1] Examples/resources for using XSLT on XHTML [2] AHAH Microformat [2] Testing Machine Configuration