fter writing the Build an Object-Oriented Tree Control Using JavaScript article, it occurred to me that using XML as the data source for the tree would be a natural fit. I wanted to create a solution that was both maintainable and extensible. That is, I wanted to create an automated system whereby any XML document that adhered to a given grammar would easily become an HTML tree control. Essentially, I wanted to be able to view the XML document in a browser and have the browser render it as a tree similar to the TreeView control in Windows Explorer. Further, the same control should work in both Internet Explorer 6 (IE) and Netscape/Mozilla (N7).
Getting Started
Initially, I set about designing a JavaScript-only solution and immediately ran into problems. While N7 parses XML according to the W3C's DOM, IE uses a proprietary ActiveX-based parser to parse XML documents. It felt like I was traveling backward in time when developers had to support different flavors of the major browserseach with its own DOM. Rather than build a script for each browser, I decided to attack the problem from a different level.
The problem was really pretty simple. I wanted to take an XML representation of a tree and transform it into an HTML representation. Transformation was the key! One XML technologyXSLT (Extensible Stylesheet Language Transformations)is perfect for this task. To provide an XSLT implementation you must include a number of different components in the solution:
<!ELEMENT tree (branch+)>
<!ELEMENT branch (branchText,(branch|leaf)*)>
<!ATTLIST branch id CDATA #REQUIRED>
<!ELEMENT branchText (#PCDATA)>
<!ELEMENT leaf (leafText,link)>
<!ELEMENT leafText (#PCDATA)>
<!ELEMENT link (#PCDATA)>
Save the file as tree.dtd. <?xml version="1.0" ?>
<tree>
<branch id="html">
<branchText>HTML</branchText>
<leaf>
<leafText>Tags, Tags, Tags</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Hyperlinks</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Images</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Tables</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Forms</leafText>
<link>#</link>
</leaf>
</branch>
<branch id="css">
<branchText>CSS</branchText>
<leaf>
<leafText>Inline Styles</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Document Wide Styles</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>External Style Sheets</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Formatting Text</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Positioning Text</leafText>
<link>#</link>
</leaf>
</branch>
<branch id="javascript">
<branchText>JavaScript</branchText>
<leaf>
<leafText>The Basics</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Working with Images</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Controlling Frames</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Browser Windows</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Form Validation</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Handling Events</leafText>
<link>#</link>
</leaf>
</branch>
<branch id="dhtml">
<branchText>DHTML</branchText>
<leaf>
<leafText>Object Detection</leafText>
<link>#</link>
</leaf>
<branch id="animation">
<branchText>Animation</branchText>
<leaf>
<leafText>Path Animation</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>
Point To Point Animation</leafText>
<link>#</link>
</leaf>
</branch>
<leaf>
<leafText>Menus</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Tabbed User Interface</leafText>
<link>#</link>
</leaf>
<leaf>
<leafText>Trees</leafText>
<link>#</link>
</leaf>
</branch>
</tree>
Save the file as tree.xml. The preceding file is called an instance of the tree DTD because it adheres to the grammar specified in the DTD. To validate the documentthat is, to make sure that it adheres to the grammaradd a DOCTYPE declaration to the document just beneath the <?xml version="1.0"?> prolog: <!DOCTYPE tree SYSTEM 'tree.dtd'>
You'll need a validating parser to validate the document. I've been working on an XML editor, written in Java, which includes validation services. You can download the editor here.
Create JavaScript and CSS Files
You'll need to create the JavaScript and CSS files next. The JavaScript file contains the variables and functions to make the tree work in a browser and the CSS stylesheet controls how the browser formats the text in the tree control.
Here's the content of the JavaScript file:
var openImg = new Image();
openImg.src = "open.gif";
var closedImg = new Image();
closedImg.src = "closed.gif";
function showBranch(branch){
var objBranch =
document.getElementById(branch).style;
if(objBranch.display=="block")
objBranch.display="none";
else
objBranch.display="block";
swapFolder('I' + branch);
}
function swapFolder(img){
objImg = document.getElementById(img);
if(objImg.src.indexOf('closed.gif')>-1)
objImg.src = openImg.src;
else
objImg.src = closedImg.src;
}
Save the completed JavaScript file as xmlTree.js. body{
font: 10pt Verdana,sans-serif;
color: navy;
}
.trigger{
cursor: pointer;
cursor: hand;
display: block;
}
.branch{
display: none;
margin-left: 16px;
}
a{
text-decoration: none;
}
a:hover{
text-decoration: underline;
}
Save the completed CSS file as xmlTree.css.
Create the XSLT Stylesheet
XSLTas the "Transformations" aspect of the name suggestsis a mechanism whereby your XML data is transformed into some other form. That form could be HTML, WML, SOAP, or another XML structure. This article focuses on converting the XML document containing the data to HTML. The concept is simple: you create a set of rules for the elements in your XML document (aka a stylesheet) that manipulates those elements to make them presentation-friendly. Then you apply the stylesheet to the document with an XSLT processor. The transformation can happen in place when XSLT support is built into the client, or externally, for example on your Web server. If you perform the transformation externally, you send the transformation's result to the client. However, both IE and N7 can be used to process the XSLT instructions in place, so if you're using one of those two browsers, you don't need a Web server to use the tree control presented in this article.
In a nutshell, XSLT transforms your XML into a more viewable form. The original XML content is known as the source tree in the XSLT process. The output of the transformation is known as the result tree. Your stylesheet contains the rules for making the switch from source tree to result tree happen. You define those rules in XSLT templates. The XSLT engine applies the rules you define.
Here is the XSLT Stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"></xsl:output>
<xsl:template match="/">
<html>
<head>
<title>XML Tree Control</title>
<link rel="stylesheet" type="text/css"
href="xmlTree.css"/>
<script type="text/javascript"
src="xmlTree.js"></script>
</head>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="tree">
<body>
<xsl:apply-templates/>
</body>
</xsl:template>
<xsl:template match="branch">
<span class="trigger">
<xsl:attribute name="onClick">
showBranch
('<xsl:value-of select="@id"/>');
</xsl:attribute>
<img src="closed.gif">
<xsl:attribute name="id">I
<xsl:value-of select="@id"/>
</xsl:attribute>
</img>
<xsl:value-of select="branchText"/>
<br/>
</span>
<span class="branch">
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="leaf">
<img src="doc.gif"/>
<a>
<xsl:attribute name="href" >
<xsl:value-of select="link"/>
</xsl:attribute>
<xsl:value-of select="leafText"/>
</a><br/>
</xsl:template>
<!-- avoid output of text node
with default template -->
<xsl:template match="branchText"/>
</xsl:stylesheet>
Save the file as xmlTree.xsl.
How the XSLT Stylesheet Works
To begin a transformation, an XSLT processor first looks for a template that matches the root node of the document. It's customary to use the root template as the container for all the output that will follow. In the preceding stylesheet, the template matching the root is declared as follows (note that the root node sits one level higher in the document than the root element):
<xsl:template match="/">
<html>
<head>
<title>XML Tree Control</title>
<link rel="stylesheet" type="text/css"
href="xmlTree.css"/>
<script type="text/javascript"
src="xmlTree.js"></script>
</head>
<xsl:apply-templates/>
</html>
</xsl:template>
Each template provides two kinds of instructions: literals that the stylesheet will write without alteration directly to the result tree, and instructions that inform the XSLT processor to perform some action. You may have noticed the <xsl:apply-templates/> element above. That's an instruction to the XSLT processor to move forward and process the children of the current node. As the processor processes the node's children, it searches for templates that match those children and executes the instructions in the matching template. <xsl:template match="branch">
<span class="trigger">
<xsl:attribute name="onClick">
showBranch
('<xsl:value-of select="@id"/>');
</xsl:attribute>
<img src="closed.gif">
<xsl:attribute name="id">I
<xsl:value-of select="@id"/>
</xsl:attribute>
</img>
<xsl:value-of select="branchText"/>
<br/>
</span>
<span class="branch">
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<xsl:apply-templates/>
</span>
</xsl:template>
The branch template creates a <span> element and adds an onClick attribute. The stylesheet gets the value for the onClick attribute from the id attribute of the <branch> node (the current node). Next, the template creates an image for each <span> element (trigger) with a unique idthe sample code uses an 'I' as a prefix and appends the id attribute value. The id attribute ensures that JavaScript (which acts on the result of the transformation) will be able to determine which trigger a user clicked so it can swap the src attribute value to display the correct image. The final <span> represents the branch to expand in the browser. Once again, sometimes it's easier to analyze what's happening in a transformation by looking at the results: <html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>XML Tree Control</title>
<link href="xmlTree.css" type="text/css"
rel="stylesheet">
<script src="xmlTree.js"
type="text/javascript">
</script>
</head>
<body>
<span class="trigger" onclick="
showBranch('html'); ">
<img src="closed.gif" id="Ihtml">HTML<br>
</span>
<span class="branch" id="html"><img
src="doc.gif">
<a href="#">Tags, Tags, Tags</a><br>
<img src="doc.gif">
<a href="#">Hyperlinks</a><br>
<img src="doc.gif"><a href="#">Images</a><br>
<img src="doc.gif"><a href="#">Tables</a><br>
<img src="doc.gif"><a href="#">Forms</a><br>
</span>
<span class="trigger" onclick="
showBranch('css'); ">
<img src="closed.gif" id="Icss">CSS<br>
</span>
<span class="branch" id="css"><img
src="doc.gif">
<a href="#">Inline Styles</a><br>
<img src="doc.gif"><a href="#">Document
Wide Styles</a><br>
<img src="doc.gif"><a href="#">External
Style Sheets</a><br>
<img src="doc.gif"><a href="#">Formatting
Text</a><br>
<img src="doc.gif"><a href="#">Positioning
Text</a><br>
</span>
<span class="trigger" onclick="
showBranch('javascript'); ">
<img src="closed.gif"
id="Ijavascript">JavaScript<br>
</span>
<span class="branch" id="javascript"><img
src="doc.gif">
<a href="#">The Basics</a><br>
<img src="doc.gif"><a href="#">Working
with Images</a><br>
<img src="doc.gif"><a href="#">Controlling
Frames</a><br>
<img src="doc.gif"><a href="#">Browser
Windows</a><br>
<img src="doc.gif"><a href="#">Form
Validation</a><br>
<img src="doc.gif"><a href="#">Handling
Events</a><br>
</span>
<span class="trigger" onclick="
showBranch('dhtml'); ">
<img src="closed.gif" id="Idhtml">DHTML<br>
</span>
<span class="branch" id="dhtml"><img
src="doc.gif">
<a href="#">Object Detection</a><br>
<span class="trigger" onclick="
showBranch('animation'); ">
<img src="closed.gif"
id="Ianimation">Animation<br>
</span>
<span class="branch" id="animation"><img
src="doc.gif">
<a href="#">Path Animation</a><br>
<img src="doc.gif"><a href="#">Point To
Point Animation</a><br>
</span>
<img src="doc.gif"><a href="#">Menus</a><br>
<img src="doc.gif"><a href="#">Tabbed User
Interface</a><br>
<img src="doc.gif"><a href="#">Trees</a><br>
</span>
</body>
</html>
The only thing left to do is to attach the XSLT stylesheet to the xmlTree.xml XML document. Just beneath the <?xml version="1.0"?> prolog, add the following processing instruction: <?xml-stylesheet type="text/xsl" href="xmlTree.xsl"?>
After altering the xmlTree.xml file (and saving the changes), you can load it into either IE or N7 to see the tree control work. I tested the sample code with IE 6 and Mozilla 1.3 and both browsers rendered the tree beautifully. Just make sure that all the files are in the same directory.
Embed the Tree in an HTML Document
In most cases, the tree control will be a component in another Web page. The transformation above creates the tree for viewing in a browser but, usually, you'll want to surround the tree with other content.
Both IE and N7 use the same mechanism for embedding external contentthe <iframe> element. Here's a simple method for embedding the tree in another page:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Embedded XML Tree Control</title>
<style type="text/css">
#tree{
border-right:1px black solid;
border-top: none;
border-bottom: none;
border-left:none;
position: absolute;
top:10;
left:0;
width: 150;
}
#content{
position:absolute;
top:0;
left: 160;
font-family: sans-serif;
color: navy;
}
</style>
<script type="text/javascript">
function setHeight(){
var availableHeight = 0;
if(document.all)
availableHeight = document.body.clientHeight;
else
availableHeight = innerHeight;
var tree = document.getElementById('tree').style;
tree.height = availableHeight-20;
}
</script>
</head>
<body onload="setHeight()">
<iframe id="tree" src="tree.xml"></iframe>
<div id="content">
<h2>Page Content Here</h2>
</div>
</body>
</html>
In my opinion, Mozilla renders this HTML file correctly but IE doesn't. IE insists on displaying a scrollbar for the <iframe>even when it isn't necessary.
| DevX is a division of Internet.com. © Copyright 2010 Internet.com. All Rights Reserved. Legal Notices |