Yahoo! Developer Network Home - Help

YUI Library Examples: TreeView Control: Dynamically Loading Node Data

TreeView Control: Dynamically Loading Node Data

In many cases, you'll want to avoid rendering your TreeView Control with a full dataset. Rather, you'll want to load all visible nodes immediately and then retrieve data only when needed for nodes that aren't visible when the control is first loaded. This example shows you how to do that.

In the TreeView instance below, we've loaded all "top-level" nodes into the page as soon as the page loads; these nodes contain the names of many Indian states. When a node is expanded, we use Connection Manager to access a Yahoo! Search web service that will return a list of "related suggestions." So when the page loads, we know nothing about our top-level nodes' children. And while the resulting TreeView instance could grow quite large through user interaction, we need only load a very light set of nodes to begin with.

This example also shows the two label styles for childless nodes. The first (default) style maintains the expand/collapse icon style even when the node has no children; the second style shows childless nodes as leaf nodes with no expand/collapse icon. Note: this is for dynamically loaded nodes after the dynamic load process has produced zero children. You can also force the leaf node presentation for any node by setting the isLeaf property to true (this also disables dynamic loading).

Childless Node Style:

Using TreeView with Connection Manager to Dynamically Load Data

Dynamic loading of a TreeView Control's child nodes allows you to optmize performance by only loading data for and creating the nodes that will be visible when the tree is rendered. Nodes that are not expanded when the TreeView's draw method is invoked are left childless in the initial state. When such a node is expanded (either by user action or by script), a dynamic loader function is called. That function has three important roles:

  1. Check for child nodes: The dynamic loader function will check for child nodes by evaluating in-page data (for example, data held in a JavaScript array or object) or by retrieving data about the expanding node from the server via XMLHttpRequest. In the example on this page, We'll use the YUI Connection Manager component to check for data from a web service.
  2. Add child nodes, if present: If it determines that child node's are present for the expanding node, the dynamic loader must add those child nodes to the TreeView instance. Because these nodes are only added when needed, the overall complexity of the initial TreeView (in JavaScript and in the DOM) is reduced and its render time is much faster.
  3. Invoke the expanding node's callback method: Once the dynamic loader method determines whether the expanding node has children (and adds any children that may be present), it must notify the expanding node's object that dynamic loading is complete. It does this via a callback function which is passed into the dynamic loader as an argument.

Here's how the code on this page manages those three steps. First, we markup the page with a target element into which the TreeView's DOM structure will be injected:

1<div id="treeDiv1"></div> 
view plain | print | ?

Next, we build a function that creates our initial TreeView:

1function buildTree() { 
2   //create a new tree: 
3   tree = new YAHOO.widget.TreeView("treeDiv1"); 
4    
5   //turn dynamic loading on for entire tree: 
6   tree.setDynamicLoad(loadNodeData, currentIconMode); 
7    
8   //get root node for tree: 
9   var root = tree.getRoot(); 
10    
11   //add child nodes for tree; our top level nodes are 
12   //all the states in India: 
13   var aStates = ["Andhra Pradesh","Arunachal Pradesh","Assam","Bihar","Chhattisgarh","Goa","Gujarat","Haryana","Himachal Pradesh","Jammu and Kashmir","Jharkhand","Karnataka"]; 
14    
15   for (var i=0, j=aStates.length; i<j; i++) { 
16        var tempNode = new YAHOO.widget.TextNode(aStates[i], root, false); 
17   } 
18 
19   // Use the isLeaf property to force the leaf node presentation for a given node. 
20   // This disables dynamic loading for the node. 
21   var tempNode = new YAHOO.widget.TextNode('This is a leaf node', root, false); 
22   tempNode.isLeaf = true
23 
24   //render tree with these toplevel nodes; all descendants of these nodes 
25   //will be generated as needed by the dynamic loader. 
26   tree.draw(); 
27
view plain | print | ?

We have turned on dynamic loading (in line 6 above) at the TreeView level rather than on a specific node, so every expanding node now will invoke our dynamic load handler (loadNodeData). That means that before the node expands, the node object will be passed to loadNodeData along with a callback function and the expansion won't take place until we fire that callback. That gives us a chance to load child nodes before the expand action occurs.

We'll use Connection Manager to get external data. Here's our loadNodeData function, with comments describing what happens at each step.

1function loadNodeData(node, fnLoadComplete)  { 
2     
3    //We'll create child nodes based on what we get back when we 
4    //use Connection Manager to pass the text label of the  
5    //expanding node to the Yahoo! 
6    //Search "related suggestions" API.  Here, we're at the  
7    //first part of the request -- we'll make the request to the 
8    //server.  In our Connection Manager success handler, we'll build our new children 
9    //and then return fnLoadComplete back to the tree. 
10     
11    //Get the node's label and urlencode it; this is the word/s 
12    //on which we'll search for related words: 
13    var nodeLabel = encodeURI(node.label); 
14     
15    //prepare URL for XHR request: 
16    var sUrl = "assets/ysuggest_proxy.php?query=" + nodeLabel; 
17     
18    //prepare our callback object 
19    var callback = { 
20     
21        //if our XHR call is successful, we want to make use 
22        //of the returned data and create child nodes. 
23        success: function(oResponse) { 
24            YAHOO.log("XHR transaction was successful.""info""example"); 
25            console.log(oResponse.responseText); 
26            var oResults = eval("(" + oResponse.responseText + ")"); 
27            if((oResults.ResultSet.Result) && (oResults.ResultSet.Result.length)) { 
28                //Result is an array if more than one result, string otherwise 
29                if(YAHOO.lang.isArray(oResults.ResultSet.Result)) { 
30                    for (var i=0, j=oResults.ResultSet.Result.length; i<j; i++) { 
31                        var tempNode = new YAHOO.widget.TextNode(oResults.ResultSet.Result[i], node, false); 
32                    } 
33                } else { 
34                    //there is only one result; comes as string: 
35                    var tempNode = new YAHOO.widget.TextNode(oResults.ResultSet.Result, node, false
36                } 
37            } 
38                                 
39            //When we're done creating child nodes, we execute the node's 
40            //loadComplete callback method which comes in via the argument 
41            //in the response object (we could also access it at node.loadComplete, 
42            //if necessary): 
43            oResponse.argument.fnLoadComplete(); 
44        }, 
45         
46        //if our XHR call is not successful, we want to 
47        //fire the TreeView callback and let the Tree 
48        //proceed with its business. 
49        failure: function(oResponse) { 
50            YAHOO.log("Failed to process XHR transaction.""info""example"); 
51            oResponse.argument.fnLoadComplete(); 
52        }, 
53         
54        //our handlers for the XHR response will need the same 
55        //argument information we got to loadNodeData, so 
56        //we'll pass those along: 
57        argument: { 
58            "node": node, 
59            "fnLoadComplete": fnLoadComplete 
60        }, 
61         
62        //timeout -- if more than 7 seconds go by, we'll abort 
63        //the transaction and assume there are no children: 
64        timeout: 7000 
65    }; 
66     
67    //With our callback object ready, it's now time to  
68    //make our XHR call using Connection Manager's 
69    //asyncRequest method: 
70    YAHOO.util.Connect.asyncRequest('GET', sUrl, callback); 
71
view plain | print | ?

In the codeblock above, we set up our XHR call using Connection Manager and provide the functions that should handle the data that comes back. Here are a few important items to note:

  1. We pass the node and our TreeView callback into our success and failure handlers in the argument member of the Connection Manager callback ojbect. That allows us to access those important pieces once we get data back from the XHR transaction.
  2. This process is asynchronous. loadNodeData completes and returns after it fires off the request via YAHOO.util.Connect.asyncRequest. At a later time, Connection Manager fires either the success or failure function we passed in
  3. We fire our fnLoadComplete function from both success and failure handlers. Whether the request succeeds or not, we want TreeView to stop waiting for it at some point. So, if Connection Manager fires our failure handler, we'll treat that the same way we treat a node that has no children — we fire fnLoadComplete and move on.

YUI Logger Output:

Logger Console

INFO1ms (+1) 11:10:09 PM:

global

Logger initialized

Note: You are viewing this example in debug mode with logging enabled. This can significantly slow performance.

Reload with logging
and debugging disabled.

More TreeView Control Resources:

Copyright © 2008 Yahoo! Inc. All rights reserved.

Privacy Policy - Terms of Service - Copyright Policy - Job Openings