Article
Build an Ajax Tree with YUI
jQuery may be the cool kid on the block when it comes to JavaScript libraries, but there are a number of serious contenders out there—not the least of which is Yahoo’s YUI (Yahoo User Interface) library. It includes a full range of functionality: DOM manipulation, Ajax helpers, rich interface widgets … the works!
When you code with YUI, you’re leveraging a ton of freely available, proven code with supporting documentation. You should consider yourself fairly safe using code backed by one of the biggest players on the Web.
In this article we’ll look at two separate pieces of the YUI puzzle, and how they can be combined to create a cool piece of functionality. Specifically, we’ll be working with the YUI TreeView component, which is a way of presenting a dynamic hierarchical tree in the browser. But we’ll also be looking into YUI’s Ajax helpers, as we’ll be fleshing out the limbs of the tree dynamically with Ajax calls.
The project we’ll undertake in this article will create an instance of the TreeView component that has only one text node to begin with. When the user clicks on this node, an Ajax request is fired off to find a list of synonyms for the clicked word. Our code will create new nodes on the tree for each synonym returned. Each new node, in turn, will be clickable to create its own subtree of synonyms.
Before undertaking this project, you’ll want to have at least some literacy or skill in JavaScript, especially object oriented JavaScript (for an extensive introduction to the latter, you can check out this article by Ryan Frishberg).
The complete ZIP code archive for this tutorial can be downloaded here.
The core method we’ll be using in this tutorial is the asynchronous call to the synonym-searching script. We use the YUI Connection Manager to make that call:
YAHOO.util.Connect.asyncRequest('GET', stringURL, objectCallBack, null);
As this is an asynchronous request, once the call is made, control
flow immediately returns to execute statements following
asyncRequest.
Let’s have a look at the arguments to this method. The first two
arguments consist of the type of the request (GET, in
this case) and the URL to which the request is being made. We’ll come to
the third argument in a moment, and argument four is only required for
POST transactions, so that stays
null for our example.
The key argument is objectCallBack. Here’s
its structure:
{
success: funcToDoOnSuccess,
failure: funcToDoOnFailure,
argument: { "argForSuccessOrFailureFuncs": myObject },
timeout: 7000
};
We first need to define methods to be run on a successful request
(success), or an error
(failure). In the example above, the
asyncRequest method calls the function
funcToDoOnSuccess if its GET
request is successful; for an unsuccessful GET request,
it will call the function funcToDoOnFailure (for
example, in the event of a 404 Page Not Found error).
In the argument property we place the data that
these methods will need to do their jobs. When the
asyncRequest method calls either of our callback
functions (funcToDoOnSuccess or
funcToDoOnFailure), it will use whatever you
specify here as the argument to pass to that function.
As we work through the tutorial, we’ll be replacing the above generic example parameters with the ones needed to build our synonym tree.
Before we can use any YUI objects, we have to link to the YUI JavaScript library. Fortunately, Yahoo provides Content Delivery Network (CDN) hosting for all the JavaScript and CSS files required, as well as providing a great interface for creating a custom link that will include only the needed functionality.
Head over to the Yahoo Dependency Configurator, and select from the YUI JavaScript Utilities section, as well as from the YUI User Interface Widgets section. At the bottom of the page you’ll be provided with a snippet of code like this:
<!-- Combo-handled YUI CSS files: --> <link rel="stylesheet" type="text/css" href="…"> <!-- Combo-handled YUI JS files: --> <script type="text/javascript" src="…"></script>
I’ve omitted the URLs from the above code example as they’re extremely long, and it’s best to construct your own URL with the functionality you need. The advantage of this is that you can easily include any other YUI components you need without adding additional style sheets or script files. Just head back to the configuration app and generate a new set of links!
Simply copy the HTML snippet you receive into the head of your document, and you’re set to make a
start with YUI.
The first step is to create a function that will build the TreeView widget object. Initially, it will contain just one text node containing the label "apple." When the user clicks that node, our code will build a subtree under it, populating it with synonyms for “apple.”
In the following code fragment, notice first the lines without bold.
We create the tree with the TreeView’s constructor, whose argument is the
HTML element in which we’ll build the tree (AjaxTreeDiv). The
getRoot call receives a reference to the tree’s
root and passes it to the TextNode’s constructor. Giving the root to the
new node connects the tree; it creates a link back to the parent. We do
the actual drawing of the tree with its render method.
We start by declaring some variables. obNode will
be the node object, obAjaxTree will be the tree object,
and treeRoot will be used to hold a reference to the
tree’s root node.
We call the TreeView’s constructor
(YAHOO.widget.TreeView), passing in the HTML
element in which we want to build the tree (AjaxTreeDiv).
The highlighted statement is the one that should grab most of our
attention. The setDynamicLoad method tells the
tree that we want to know when the user clicks on one of its nodes to
expand it, and it tells the tree what function to call
(makeMoreNodes, which we’ll be writing shortly)
when those clicks happen:
function buildAjaxTree() {
var obNode;
var obAjaxTree;
var treeRoot;
obAjaxTree = new YAHOO.widget.TreeView ("AjaxTreeDiv");
obAjaxTree.setDynamicLoad(makeMoreNodes,0);
treeRoot = obAjaxTree.getRoot();
obNode = new YAHOO.widget.TextNode("apple", treeRoot, false);
obAjaxTree.render();
}
After setting that option, we store a reference to the tree’s root
in treeRoot, and create a new
TextNode. Passing the treeRoot
variable to the TextNode constructor connects the
node with the tree. Finally, we call the render
method to display the tree.
Notice that all of this code is inside a function, which we’ve
called buildAjaxTree. Here’s the statement that
will call it:
YAHOO.util.Event.onDOMReady(buildAjaxTree);
This is the first statement of our code that will be executed. The
onDOMReady method calls
buildAjaxTree when the HTML page is fully loaded.
Running our script before that point would invite errors.
Now let’s walk through the makeMoreNodes
function. First, refer back to the overview of the callback object
described in the beginning of this article. Remember that our Ajax call
(asyncRequest) needs a callback object with
success and failure
methods, so it can call one of those methods after its data gathering
mission. Most of the code inside makeMoreNodes
works to create that callback object.
Here’s the callback object we’ll be using. Compare it with the
generic callback object we saw when introducing
asyncRequest:
var obMkNodesCb = {
success: foundSynonyms,
failure: foundNoSynonyms,
argument: {
"node": nodeToAddTo
},
timeout: 7000
};
The success and failure
properties refer to the methods that asyncRequest
will call after it queries our server-side thesaurus script. We will call
the foundSynonyms function if the PHP script
succeeds in pulling in some synonyms, or the
foundNoSynonyms callback if the PHP script fails
in its search. Note that the timeout property is a
factor in this failure case: asyncRequest flags a
failure if it fails to receive results within seven seconds (7,000
milliseconds) of being called.
The asyncRequest method requires that the
argument property be a part of the callback object.
Remember that the argument property contains whatever
data is needed by the success and
failure functions called by
asyncRequest. For our example, prior to the Ajax
call, we store the node clicked by the user in
argument. The success method
needs this node for two reasons. Firstly, to build the new synonym
subtree: a root node is needed for this, and the node clicked by the user
will be that root. Secondly, to tell the node we’re done using it, through
its loadComplete method. If we didn’t fire that
method, the tree would freeze, because one of its nodes wouldn’t know when
to resume listening for the user’s mouse clicks.
The failure method needs to have access to the
clicked node for the same reason. Even though the
failure method adds no nodes to the tree, the node
the user clicked on still needs its loadComplete
method called, so it can start listening for user clicks again.
Darrin Koltow is a freelance developer and writer, and webmaster of