AjaxTree.com uses the DOJO ajax API to explain how to make ajax trees that are dynamic.

Explaination of Tags

This section will explain in detail all of the tags that you can use to create your dojo tree implementation.

Required Tags

As when using any dojo functionality you must include the dojo.js file reference
<script type="text/javascript" src="javascript/dojo.js"></script>

In order to use the dojo tree widget you must also include the following required tags:

Listing 1. Required Dojo Tree Tags<script type="text/javascript"> dojo.require("dojo.lang.*");
dojo.require("dojo.widget.Tree");
dojo.require("dojo.widget.TreeRPCController");
dojo.require("dojo.widget.TreeSelector");
dojo.require("dojo.widget.TreeNode");
dojo.require("dojo.widget.TreeContextMenu");
</script>

Constructing Your Tree

TreeSelector

The html tag to begin your tree definition and define the selector looks like this: <div dojoType="TreeSelector" widgetId="AnyNameYouWant"> It is the parent div tag that surrounds the dojo tree specifications.

Tree Selector Properties

PropertyDescription
dojoType This is the html element that specifies that the Dojo element is going to be a Tree. Therefore, it's value should be "TreeSelector".
widgetId This is the name of the Tree Selector. You can use anything you'd like. It is an identifier so you can refer to this specific Tree Selector if you have more than one on your page.

Tree

The html tag to begin your tree definition and define the tree looks like this: <div dojoType="Tree" widgetId="AnyNameYouWantForTheTree" selector="SameNameYouUsedForYourSelectorID" toggler="plain"> It is the parent div tag that surrounds all of the children nodes but is within the Tree Selector parent tags.

PropertyDescription
dojoType This is the html element that specifies that the Dojo element is going to be a Tree. Therefore, it's value should be "TreeSelector".
widgetId This is the name of the Tree. You can use anything you'd like, just make sure it's unique to your html page. It is an identifier so you can refer to this specific Tree if you have more than one on your page.
selector This is the name of the Tree Selector to use for this tree. It should be the exact same name as the one used for the widgetId for your TreeSelector div tag.
toggler This tag refers to the toggle dojo functions you can use defined in dojo.lfx.toggle. These functions include: plain, fade, wipe, and explode as of Dojo 0.3.1. They are basically the animation effects used when expanding and collapsing tree nodes.
controller Widget ID of an TreeController that adds additional functionality to this tree through event handlers. During initialization, the widget's "subscribeTree" method will be called with this tree as its argument.
selector Optional widget ID of an TreeSelector. If none is specified, initialization will build one and assign it to the "selector" property of this tree. The default selector only supplies a "selectedNode" property for use by the tree's controller. I think this object's purpose is to allow one selected node for an entire set of trees.
tree Object. This tree. Also used by "TreeNode".
isExpanded Boolean. "true". The tree root node is considered to be always expanded.
isTree Boolean. Always "true".
showGrid Boolean, default:"true". Controls display of grid lines connecting tree nodes. Settable only on creation.
showRootGrid Boolean, default: "true". Controls display of grid lines to top-level tree nodes. Settable only on creation.
menuUsed to specify the TreeContextMenu that is to be used by this tree
DNDModeDrag and Drop Mode ex. DNDMode="between" -- this allows the user to drop a tree node between other tree nodes.
actionsDisabledUsed to specify which actions should be disabled for the tree. If you don't the user to be able to perform some action specify that action's name here ex. actionsDisabled="addChild"
DNDAcceptTypesDrag and Drop Accept Types ex. DNDAcceptTypes="secondTree"

TreeNode

The last thing you need to make any tree complete are the children nodes or tree nodes. The tree nodes inherit their properties from the dojo.widget.HtmlWidget. <div dojoType="TreeNode" widgetId="AnyNameYouWantForThisNode" title="Some Title that's displayed to the user" isFolder="false">

PropertyDescription
dojoTypeThis is the html element that specifies that the Dojo element is going to be a TreeNode. Therefore, it's value should be "TreeNode".
widgetIdThis is the name of the Tree Node. You can use anything you'd like, just make sure it's unique to your html page. It is an identifier so you can refer to this specific Tree Node if you have more than one on your page.
titleThis is the title or main lable of the Tree Node as will be viewable by the user. Some users find it helpful to use an outline type of naming convension. 1 for parent 1.1; 1.2; etc. for children of 1, etc. The title can also include html
isFolderOptional Tag. Default value is false. When this value is true it forces an expand/collapse image on every node, even if it doesn't have any children. By setting it to false the expand/collapse image will only show up on a node if it has children. If it does not have a custom childIconSrc, it will display with a "folder" icon.
childIconSrc If specified, a string URL pointing to the icon for the node.
afterLabelString containing HTML to be rendered following the node title.
objectIdString identifying the represented object, for access from JavaScript.
treeObject. The containing tree.
isTreeFalse.
isTreeNodeTrue.
isExpandedIndicates whether the node is expanded. Always false if there are no children.
childIconFolderSrcDefault URL, used for nodes that are folders if no childIconSrc is specified.
childIconDocumentSrcDefault URL, used for nodes that are not folders if no childIconSrc is specified.
objectAvailable to store application data, an object representing the represented object.

Using JavaScript to Manipulate Dojo Trees and Tree Nodes

It is also possible to use JavaScript to dynamically create a Dojo Tree. You can do this by creating a script such as the following:
Listing x. Dynamic Dojo Tree<script type="text/javascript"> var tree = dojo.widget.createWidget("Tree", {}); for (var i=0; i<3; i++) { var node = dojo.widget.createWidget("TreeNode", {title: "node"+i}); tree.addChild(node); } </script>
The above code would be the same as doing this:
<div dojoType="Tree" widgetId="treeWidget" selector="treeSelector" toggler="wipe"> <div dojoType="TreeNode" widgetId="node0" title="node0"></div> <div dojoType="TreeNode" widgetId="node1" title="node1"></div> <div dojoType="TreeNode" widgetId="node2" title="node2"></div> </div>
both cases create a parent Tree with three tree nodes.

Summary of Javascript Methods for Dojo Tree Manipulation

The following JavaScript methods can be used to query or change the dojo tree in some way or another.

JavaScript MethodDescription
addChild(child, index)Adds child to the tree at the index given. If no index is given (it is an optional parameter), the child is appended to the end of the list of children. It is also important to note that the child you are adding can't already be a part of the tree, i.e. it must have a unique widgetId.
removeChild(child)Remove the child from the tree.
initializeInitialization protocol. Interprets arguments "selector" and "controller", etc.. TBD.
postCreateCompletes initialization of all tree nodes attached to this tree.
getInfoReturns an object with properties "widgetId", this tree's widget ID; and "objectId", this tree's "objectId" property. Overridable.

Summary of Javascript Methods for Dojo Tree Node Manipulation

Similar to the above Dojo Tree JavaScript methods, the following JavaScript methods apply to the Dojo Tree Nodes

JavaScript MethodDescription
addChild(child, index)Adds the given child to this node. You can apply this method to a node removed from another tree or a newly-created node, and this will adjust its display and tree relationships.
removeChild(child)Removes the given child from this tree node.
setFolderAsserts that this tree node is a folder, regardless of whether it has children currently or not.
expandShow any children and mark the node as expanded.
collapseHide any children and mark the node as not expanded.
editTakes an object, which it treats as a collection of property-value mappings. Updates both widget and DOM state for properties

Summary of JavaScript Events for Dojo Tree Nodes

When constructing a Dojo Tree you can set actions to occur when some event happens on a tree node:

EventsDescription
onTreeClick Called when the expander icon is clicked. Event properties are "source": the node; "event" the triggering click event. Also publishes to tree.eventNames.treeClick.
onIconClick Called when the node's icon is clicked. Event properties are "source": the node; "event" the triggering click event. Also publishes to tree.eventNames.iconClick.
onTitleClick Called when the node's title is clicked. Event properties are "source": the node; "event" the triggering click event. Also publishes to tree.eventNames.titleClick.

Styling the Tree Nodes

You can style the tree nodes by applying some css style tags or any other html tag. Some popular examples include:

  • Making it red title="<span style='color:red'>Node Title Text Goes Here "
  • A Node Link title="<a href='linkLocation.html'>Node Title Text Goes Here "
  • A check box title="<input type='checkbox'>Node Title Text Goes Here "

TreeRPCController

This class, together with Tree, TreeSelector, and TreeNode, provide a tree widget with nodes that can be dynamically added, modified, and deleted. See the Complex Dojo Tree Example below to see the TreeRPCController in action.

Controller Properties

PropertyDescription
RPCUrlString URL of a page that generates information for dynamic loading of tree nodes. "action=xxx" and node-specific information is always added in the request's query string.
DNDModeDefault "off". Controls the mode of drag-and-drop. Other possible modes are "onto" and "between".
eventNamesObject with properties select, deselect, collapse, expand, dblselect, move, remove. TODO: document these.
dragSourcesTBD
dropTargetsTBD
errorHandlerFunction to be called in case of errors in the internal I/O. Used exactly as an error handler for dojo.io.bind.
DNDController ex. DNDController="create"

Initialization Protocol for Tree Controller

PropertyDescription
initializeSets up this controller's event names as widgetId+"/"+eventName.

Methods for Tree Controller

MethodDescription
subscribeTree(tree)Subscribes this controller to the topics specified by the tree's eventNames: nodeCreate, treeClick, iconClick, and titleClick. They fire the controllers matching onNodeCreate, onTreeClick, onIconClick, and onTitleClick events. Normally called by the tree during its creation.
loadRemote(node, sync, callback, callObj)Issues a request for loading the node's children. Requests the page specified by the RPCUrl with action=getChildren and data=xxx, where the value is the JSON representation of an object with two properties: "node" and "tree". The value of the "node" is the result of node.getInfo(), and the value of the "tree" is the result of tree.getInfo(). Passes the received JSON object to loadProcessResponse. Other parameters TBD.
loadProcessResponse(type, node, result, callback, callObj)Type is supplied by "dojo.io.bind" as the response type. Node is the tree node. Result is the object created from the response JSON data. The JSON data is an array containing objects. This supplies each object as the information argument to dojo.widget.createWidget, and specifies the node's "widgetType" as the widget type for all new children. This adds the new children to the node and mark's the node as loaded. Finally if a callback is supplied, this calls it so that the callObj is "this", passing the node and new children as arguments. The callObj defaults to being the controller.
expand(node, callObj, callFunc, sync)Normally called from onTreeclick. If the node's load state is "unchecked", calls loadRemote, with a callback function that expands the parent node when loading is done. Otherwise call's the node's expand method and publishes on the eventNames.expand topic. In all cases if there is a callFunc, calls it with callObj as "this" and the node as the one argument.
collapse(node)If the node is expanded, calls node.collapse, then publishes on the eventNames.collapse topic.
select(node)Called from onTitleClick. Calls the node's "markSelected" method, records it as the tree selector's selectedNode, and publishes on the controller's eventNames.select topic.
deselect(node)Call's the node's unMarkSelected method, sets the selector's selectedNode to null, and publishes on the eventNames.deselect topic.

Events for Tree Controller

These events are set up by the subscribeTree method.

EventDescription
onTreeClick:This expands or collapses the appropriate node. If the node has never before been expanded and has no children, this calls "loadRemote".
onIconClick:Calls onTitleClick.
onTitleClick:If the clicked node is the tree selector's seelectedNode, publishes on the controller's dblselect topic. If there is a previously selected node, calls this controller's deselect method on that node. Then calls this controller's "select" method on the clicked node.

TreeContextMenu

An Example of using a tree context menu would be:
<div dojoType="TreeContextMenu" toggle="none" contextMenuForWindow="false" widgetId="treeContextMenu"></div>
The Tree Context Menu is used when you'd like the users to be able to perform some action on the tree such as adding a new node, etc. In which case you'd use a Context Menu to display what those choices were to the user.

Simple Dojo Tree Example


For this bare bones example we have three nodes in a dojo tree. The second node is the only one which has child nodes. For this reason you'll notice that it is the only node of the three that has a plug [+] that allows the user to expand or collapse its' contents.

We'll now walk you through the code and what it takes to make this example possible. The following code can be copied and pasted into an html page to view the dojo tree widget in action. Don't forget you'll need to download the dojo toolkit. For this example we put the dojo source javascript under a directory named javascript.

Listing 2. Simple Dojo Tree Example <html>
<head>
<title>Simple Dojo Tree Example</title>
<script type="text/javascript" src="javascript/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.widget.Tree");
dojo.require("dojo.widget.TreeSelector");
dojo.require("dojo.widget.TreeNode");
dojo.require("dojo.widget.TreeContextMenu");
</script>
</head>
<body>
<div dojoType="TreeSelector" widgetId="treeSelector"></div>

<div dojoType="Tree" widgetId="treeWidget" selector="treeSelector" toggler="wipe">
<div dojoType="TreeNode" widgetId="1" title="First node" isFolder="false"></div>
<div dojoType="TreeNode" widgetId="2" title="Second node">
<div dojoType="TreeNode" widgetId="2.1" title="Second node First Child"></div>
<div dojoType="TreeNode" widgetId="2.2" title="Second node Second Child"></div>
</div>
<div dojoType="TreeNode" widgetId="3" title="Third node" isFolder="false"></div>
</div>
</body>
</html>

As mentioned at the beginning of the article it is necesary to have the required dojo tree javascript imports defined which are the first few sections of this code. Next we must define the tree selector giving it a unique widgetId. That tree selector is then referenced with our dojoType=Tree tags. Give the Tree a cool toggler effect of wipe and we're ready to add in some nodes. There are tree direct nodes with widgetId's 1, 2, and 3 respectively. There is no root root node. Then tree node 2 has two children. That's it, you're off to a great start with discovering what dojo tree's can do. Next we'll jump into how Dojo Tree's can be used in an SOA Environment and give a complex example.

Using Dojo Tree in an SOA Environment

Moved this to before the complex example as it seems like the article will transition well between the two

Complex Dojo Tree Example

This example is a little more complex than the SimpleDojoTreeExample just given. It uses java and a RPC call to populate the nodes of the tree. You can download the source code to this example from the bottom of this article.

This example has a root node and three child nodes. When any of the three child nodes are clicked a servlet will be called. The servlet will then check to see which node was clicked and based on that it will populate the children of that child. So, in the screen shot above, when the user clicks on a color child they will see items that are of that color. Under the Blue child the user will see a normal and a link node. If the user clicks on Sky they will be directed to www.theSky.com. Finally the Green children are unique because they call yet another jsp page that loads inside of the current screen. So when you click on Grass you'd see a message like the one displayed above. As soon as you click on another child node (Cucumber or Frogs) that information will replace the information that is currently displaying for Grass. This is really useful if you'd like to load content specifc information onto a section of the page based on the user clicking on a tree node.

Now we'll walk through the code that made this possible.

We'll start by looking that the index.html file. This is where the Dojo Tree is constructed. You'll notice that the required imports are placed at the beginning of the document as we described was necessary for the dojo trees. There are a few other JavaScript methods that we will come back to a little later. First let's just right into the body of the html:

<body>
<div dojoType="TreeRPCController" RPCUrl="GetAJAXTreeNodeServlet" widgetId="treeController"></div>
<div dojoType="TreeSelector" widgetId="treeSelector"></div>

<table class="treeTable" cellpadding="10">
<tr>
<td style="border:1px dashed black;">
<h4>Complex Dojo Tree</h4>

<div dojoType="Tree" selector="treeSelector" toggler="fade" widgetId="firstTree" controller="treeController">

<div dojoType="TreeNode" widgetId="1.1" title="RootNode" actionsDisabled="remove">
<!-- Below is where we put the Tree Nodes... -->
<div dojoType="TreeNode" widgetId="Red" title="Red" isFolder="true"></div>
<div dojoType="TreeNode" widgetId="Blue" title="Blue" isFolder="true"></div>
<div dojoType="TreeNode" widgetId="Green" title="Green" isFolder="true"></div>

</div>
</div>
</td>
</tr>
<tr><td id="partDetails"> </td></tr>
</table>
</body>

The first line of the code:
<div dojoType="TreeRPCController" RPCUrl="GetAJAXTreeNodeServlet" widgetId="treeController" ></div>

Is where the TreeRPCController is defined. This is important when using a servlet or other method to populate the nodes of a dojo tree. The RPCUrl defines the name of this action that will be invoked. In our case the GetAJAXTreeNodeServlet is called. The widgetId for this Tree Controller is "treeController" so we'll need to use that widgetId to refer to this particular controller. The DNDController is for specifying Drag and Drop Controllers. It is not present in this example because we have no enabled the drag and drop functionality for this example.

Next we go on to define the tree selector:
<div dojoType="TreeSelector" widgetId="treeSelector"></div>
Nothing new here, just set the dojoType to be a TreeSelector and gave it a unique widgetId

The next few lines of code are just setting up a table where the dojo tree will reside in to give it a visual appeal. Then we have the actual tree definition:
<div dojoType="Tree" selector="treeSelector" toggler="fade" widgetId="firstTree" controller="treeController">
You can see that the dojoType is a tree and that it points to the treeSelector defined above. The controller is the "treeController" which our TreeRPCController that tells the tree which servlet to invoke.

Following that we'll need to create the Tree Nodes themselves. For this example the direct children of the root node are hard coded like in our simple example:
<div dojoType="TreeNode" widgetId="1.1" title="RootNode">
<!-- Below is where we put the Tree Nodes... -->
<div dojoType="TreeNode" widgetId="Red" title="Red" isFolder="true"></div>
<div dojoType="TreeNode" widgetId="Blue" title="Blue" isFolder="true"></div>
<div dojoType="TreeNode" widgetId="Green" title="Green" isFolder="true"></div>

There is a RootNode with three children tree nodes. There is nothing fancy to the way they are defined and used. The fancy stuff comes into play when you click on each of these nodes!

You might've noticed what seems to be an insignificant row on the bottom of the code:
<tr><td id="partDetails"> </td></tr>
This is where we'll stick an entirely different page after the children of the Green node are clicked. You'll see exactly what we're talking about a little later. But first let's check out the servlet, com.ibm.dojotree.GetAJAXTreeNodeServlet.java.

When any of the children nodes are clicked (Red, Blue, Green) the RPCUrl is followed to the GetAJAXTreeNodeServlet class. There it's doGet method is called. Inside we see the following code (you can follow along by downloading the example at the bottom of this article where all of the source code is available):
Listing x. GetAJAXTreeNodeServlet.java HttpSession session = request.getSession();

String serializedNode = request.getParameter("data");
String resultNodes = "";

if (serializedNode.indexOf("Red") != -1){
ArrayList redThings = new ArrayList();
redThings.add("Cherries");
redThings.add("Strawberries");
resultNodes = this.getResultNodeString(redThings);
} else if (serializedNode.indexOf("Blue") != -1){
ArrayList things = new ArrayList();
things.add("<a href=http://www.thesky.com>Sky</a>");
things.add("Water");
resultNodes = this.getResultNodeString(things);
} else if (serializedNode.indexOf("Green") != -1){
String url = "<a onClick=getNodeDetails(\"Grass\")><span style=color:blue>Grass</span></a>";

ArrayList things = new ArrayList();
things.add("<a onClick=getNodeDetails(\"Grass\")><span style=color:blue>Grass</span></a>");
things.add("<a onClick=getNodeDetails(\"Cucumber\")><span style=color:blue>Cucumber</span></a>");
things.add("<a onClick=getNodeDetails(\"Frog\")><span style=color:blue>Frogs</span></a>");

resultNodes = this.getResultNodeString(things);
} else {
resultNodes = "[{title:'ERROR not a known color'}]";
}

/*
* For instance, one can call node.children = [{title:'node1'},{title:'node2'}].
* The objects will be set, but no widgets are created.
* You can also set children to nested array:
* node.children = [{title:'node1', children:[{title:'node2'}] }].
*/
response.getWriter().write(resultNodes);

As you can guess from reading the code there is a request parameter called data that has all of the serialized information about which node was clicked to invoke the servlet. If you had clicked Green, data would look like this:
{"node":{"widgetId":"Green","objectId":"","index":2,"isFolder":true},"tree":{"widgetId":"firstTree","objectId":""}}
Isn't that super cool?!? In our example we simply check to see if the name of the node appears in the string representation of the node and if it does we formulate our response. Of course there are better ways to do this but for example purposes this shows how the data request parameter is used.
Once we determine the node that was clicked we can return back personalized information about which child nodes that node has. It should start to look familiar as it's basically setting the title of each of the new children nodes so that they can be written back to the response. The comment before we send back the response explains how the string should be formatted. We also include a handy helper method that takes an ArrayList of string titles and returns a string representing all of the arraylist as siblings.
Listing x. GetAJAXTreeNodeServlet Helper Method private String getResultNodeString(ArrayList resultNodes){ String start = "["; StringBuffer toReturn = new StringBuffer(start); String seperator = " , "; String end = "]"; String titleStart = "{title:'"; String titleEnd = "'}"; for (int i=0; i<resultNodes.size(); i++){ String thisTitle = (String)resultNodes.get(i); toReturn.append(titleStart).append(thisTitle).append(titleEnd); if (i != resultNodes.size()-1){ toReturn.append(seperator); } } toReturn.append(end); return toReturn.toString(); }

Once these results are returned back to the user there is still one last sweet feature that we added in just for kicks and giggles. When you click on one of the Green node's children some information is displayed on the bottom of the screen. How did we do that? Well, that's just plain old AJAX. If you'll recall from the doGet method of the servlet we added an action to all of the Green's children
things.add("<a onClick=getNodeDetails(\"Grass\")><span style=color:blue>Grass</span></a>");
onClick=getNodeDetails("Grass"). We promised you we'd get back to the unused javascript methods in index.html and now we will.
Listing x. getNodeDetails(node) function getNodeDetails(node){
var strURL = 'nodeDetails.jsp?item=' + node;
var xmlHttpReq = false;
var self = this;
// Mozilla/Safari
if (window.XMLHttpRequest) {
self.xmlHttpReq = new XMLHttpRequest();
}
// IE
else if (window.ActiveXObject) {
self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}
self.xmlHttpReq.open('GET', strURL, true);
self.xmlHttpReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
;
self.xmlHttpReq.onreadystatechange = function() {
if (self.xmlHttpReq.readyState == 4) {
var divElement = document.getElementById('partDetails');
divElement.innerHTML = self.xmlHttpReq.responseText;
}
}

self.xmlHttpReq.send('');v }

This is some basic AJAX code here but it's simply calling the nodeDetails.jsp page with the item request parameter set to equal the name of the node that was passed into the method. Then it's placing the results from the nodeDetails.jsp page directly into the index.html document at the id partDetails (remember when we said this is where the information would be placed!!).

The nodeDetails.jsp page says:
Listing x. getNodeDetails(node) <%@ page language="java" contentType="text/html;charset=EUC-KR" %>
<html>
<head>
<title>Part Details</title>
<link rel=stylesheet href="css/general.css" type="text/css">
</head>
<body>
<% String item = request.getParameter("item"); %>

<h3><font color="green">You just clicked on a <%= item %> item! Try clicking again</font></h3>
</body>
</html>

Pretty Exciting Stuff!!

Conclusion

There are endless possibilities on what can be done with a dojo tree. Hopefully this article has piqued your interest such that you're off right now spicing up your website with the help of our two examples.

Download

Add the downloads for this article here

  • SimpleDojoTreeExample
  • ComplexDojoTreeExample

AjaxTree
Copyright AjaxTree All Rights Reserved