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).
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:
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:
1 | function 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.
1 | function 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:
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.
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 infnLoadComplete
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.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.
Copyright © 2008 Yahoo! Inc. All rights reserved.
Privacy Policy - Terms of Service - Copyright Policy - Job Openings