Yahoo! Developer Network Home - Help

YUI Library Examples: TreeView Control: Custom TreeView with Check Boxes

TreeView Control: Custom TreeView with Check Boxes

The TaskNode example implements a task list using treeview. It does so by extending the TextNode class to have additional, specific functionality. In this example, each TaskNode has three potential states: Checked, partially-checked (not all subtasks complete), and unchecked. Checking off a task automatically checks off all subtasks.

The TaskNode Subclass

This example explores the extension of TreeView via a subclass of the TextNode class. The full source of the TaskNode subclass follows:

1/**
2 * The check box marks a task complete.  It is a simulated form field 
3 * with three states ...
4 * 0=unchecked, 1=some children checked, 2=all children checked
5 * When a task is clicked, the state of the nodes and parent and children
6 * are updated, and this behavior cascades.
7 *
8 * @extends YAHOO.widget.TextNode
9 * @constructor
10 * @param oData    {object}  A string or object containing the data that will
11 *                           be used to render this node.
12 * @param oParent  {Node}    This node's parent node
13 * @param expanded {boolean} The initial expanded/collapsed state
14 * @param checked  {boolean} The initial checked/unchecked state
15 */ 
16YAHOO.widget.TaskNode = function(oData, oParent, expanded, checked) { 
17 
18    if (YAHOO.widget.LogWriter) { 
19        this.logger = new YAHOO.widget.LogWriter(this.toString()); 
20    } else { 
21        this.logger = YAHOO; 
22    } 
23 
24    if (oData) {  
25        this.init(oData, oParent, expanded); 
26        this.setUpLabel(oData); 
27        this.setUpCheck(checked); 
28    } 
29 
30}; 
31 
32YAHOO.extend(YAHOO.widget.TaskNode, YAHOO.widget.TextNode, { 
33 
34    /**
35     * True if checkstate is 1 (some children checked) or 2 (all children checked),
36     * false if 0.
37     * @type boolean
38     */ 
39    checked: false
40 
41    /**
42     * checkState
43     * 0=unchecked, 1=some children checked, 2=all children checked
44     * @type int
45     */ 
46    checkState: 0, 
47 
48    taskNodeParentChange: function() { 
49        //this.updateParent(); 
50    }, 
51 
52    setUpCheck: function(checked) { 
53        // if this node is checked by default, run the check code to update 
54        // the parent's display state 
55        if (checked && checked === true) { 
56            this.check(); 
57        // otherwise the parent needs to be updated only if its checkstate  
58        // needs to change from fully selected to partially selected 
59        } else if (this.parent && 2 == this.parent.checkState) { 
60             this.updateParent(); 
61        } 
62 
63        // set up the custom event on the tree for checkClick 
64        /**
65         * Custom event that is fired when the check box is clicked.  The
66         * custom event is defined on the tree instance, so there is a single
67         * event that handles all nodes in the tree.  The node clicked is 
68         * provided as an argument.  Note, your custom node implentation can
69         * implement its own node specific events this way.
70         *
71         * @event checkClick
72         * @for YAHOO.widget.TreeView
73         * @param {YAHOO.widget.Node} node the node clicked
74         */ 
75        if (this.tree && !this.tree.hasEvent("checkClick")) { 
76            this.tree.createEvent("checkClick"this.tree); 
77        } 
78 
79        this.subscribe("parentChange"this.taskNodeParentChange); 
80 
81    }, 
82 
83    /**
84     * The id of the check element
85     * @for YAHOO.widget.TaskNode
86     * @type string
87     */ 
88    getCheckElId: function() {  
89        return "ygtvcheck" + this.index;  
90    }, 
91 
92    /**
93     * Returns the check box element
94     * @return the check html element (img)
95     */ 
96    getCheckEl: function() {  
97        return document.getElementById(this.getCheckElId());  
98    }, 
99 
100    /**
101     * The style of the check element, derived from its current state
102     * @return {string} the css style for the current check state
103     */ 
104    getCheckStyle: function() {  
105        return "ygtvcheck" + this.checkState; 
106    }, 
107 
108    /**
109     * Returns the link that will invoke this node's check toggle
110     * @return {string} returns the link required to adjust the checkbox state
111     */ 
112    getCheckLink: function() {  
113        return "YAHOO.widget.TreeView.getNode(\'" + this.tree.id + "\'," +  
114            this.index + ").checkClick()"
115    }, 
116 
117    /**
118     * Invoked when the user clicks the check box
119     */ 
120    checkClick: function() {  
121        this.logger.log("previous checkstate: " + this.checkState); 
122        if (this.checkState === 0) { 
123            this.check(); 
124        } else { 
125            this.uncheck(); 
126        } 
127 
128        this.onCheckClick(this); 
129        this.tree.fireEvent("checkClick"this); 
130    }, 
131 
132    /**
133     * Override to get the check click event
134     */ 
135    onCheckClick: function() {  
136        this.logger.log("onCheckClick: " + this); 
137    }, 
138 
139    /**
140     * Refresh the state of this node's parent, and cascade up.
141     */ 
142    updateParent: function() {  
143        var p = this.parent; 
144 
145        if (!p || !p.updateParent) { 
146            this.logger.log("Abort udpate parent: " + this.index); 
147            return
148        } 
149 
150        var somethingChecked = false
151        var somethingNotChecked = false
152 
153        for (var i=0;i< p.children.length;++i) { 
154            if (p.children[i].checked) { 
155                somethingChecked = true
156                // checkState will be 1 if the child node has unchecked children 
157                if (p.children[i].checkState == 1) { 
158                    somethingNotChecked = true
159                } 
160            } else { 
161                somethingNotChecked = true
162            } 
163        } 
164 
165        if (somethingChecked) { 
166            p.setCheckState( (somethingNotChecked) ? 1 : 2 ); 
167        } else { 
168            p.setCheckState(0); 
169        } 
170 
171        p.updateCheckHtml(); 
172        p.updateParent(); 
173    }, 
174 
175    /**
176     * If the node has been rendered, update the html to reflect the current
177     * state of the node.
178     */ 
179    updateCheckHtml: function() {  
180        if (this.parent && this.parent.childrenRendered) { 
181            this.getCheckEl().className = this.getCheckStyle(); 
182        } 
183    }, 
184 
185    /**
186     * Updates the state.  The checked property is true if the state is 1 or 2
187     * 
188     * @param the new check state
189     */ 
190    setCheckState: function(state) {  
191        this.checkState = state; 
192        this.checked = (state > 0); 
193    }, 
194 
195    /**
196     * Check this node
197     */ 
198    check: function() {  
199        this.logger.log("check"); 
200        this.setCheckState(2); 
201        for (var i=0; i<this.children.length; ++i) { 
202            this.children[i].check(); 
203        } 
204        this.updateCheckHtml(); 
205        this.updateParent(); 
206    }, 
207 
208    /**
209     * Uncheck this node
210     */ 
211    uncheck: function() {  
212        this.setCheckState(0); 
213        for (var i=0; i<this.children.length; ++i) { 
214            this.children[i].uncheck(); 
215        } 
216        this.updateCheckHtml(); 
217        this.updateParent(); 
218    }, 
219 
220    // Overrides YAHOO.widget.TextNode 
221    getNodeHtml: function() {  
222        this.logger.log("Generating html"); 
223        var sb = []; 
224 
225        var getNode = 'YAHOO.widget.TreeView.getNode(\'' + 
226                        this.tree.id + '\',' + this.index + ')'; 
227 
228 
229        sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">'
230        sb[sb.length] = '<tr>'
231         
232        for (var i=0;i<this.depth;++i) { 
233            //sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>'; 
234            sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>'
235        } 
236 
237        sb[sb.length] = '<td'
238        sb[sb.length] = ' id="' + this.getToggleElId() + '"'
239        sb[sb.length] = ' class="' + this.getStyle() + '"'
240        if (this.hasChildren(true)) { 
241            sb[sb.length] = ' onmouseover="this.className='
242            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\''; 
243            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getHoverStyle()"'; 
244            sb[sb.length] = ' onmouseout="this.className='
245            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\''; 
246            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getStyle()"'; 
247        } 
248        sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"> '
249        //sb[sb.length] = '</td>'; 
250        sb[sb.length] = '<div class="ygtvspacer"></div></td>'
251 
252        // check box 
253        sb[sb.length] = '<td'
254        sb[sb.length] = ' id="' + this.getCheckElId() + '"'
255        sb[sb.length] = ' class="' + this.getCheckStyle() + '"'
256        sb[sb.length] = ' onclick="javascript:' + this.getCheckLink() + '">'
257        //sb[sb.length] = ' </td>'; 
258        sb[sb.length] = '<div class="ygtvspacer"></div></td>'
259         
260 
261        sb[sb.length] = '<td>'
262        sb[sb.length] = '<a'
263        sb[sb.length] = ' id="' + this.labelElId + '"'
264        sb[sb.length] = ' class="' + this.labelStyle + '"'
265        sb[sb.length] = ' href="' + this.href + '"'
266        sb[sb.length] = ' target="' + this.target + '"'
267        sb[sb.length] = ' onclick="return ' + getNode + '.onLabelClick(' + getNode +')"'
268        if (this.hasChildren(true)) { 
269            sb[sb.length] = ' onmouseover="document.getElementById(\''; 
270            sb[sb.length] = this.getToggleElId() + '\').className='; 
271            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\''; 
272            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getHoverStyle()"'; 
273            sb[sb.length] = ' onmouseout="document.getElementById(\''; 
274            sb[sb.length] = this.getToggleElId() + '\').className='; 
275            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\''; 
276            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getStyle()"'; 
277        } 
278        sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : ''
279        sb[sb.length] = ' >'
280        sb[sb.length] = this.label; 
281        sb[sb.length] = '</a>'
282        sb[sb.length] = '</td>'
283        sb[sb.length] = '</tr>'
284        sb[sb.length] = '</table>'
285 
286        return sb.join(""); 
287 
288    }, 
289 
290    toString: function() { 
291        return "TaskNode (" + this.index + ") " + this.label; 
292    } 
293 
294}); 
view plain | print | ?

YUI Logger Output:

Logger Console

INFO0ms (+0) 11:56:22 AM:

global

Logger initialized

More TreeView Control Resources:

Copyright © 2008 Yahoo! Inc. All rights reserved.

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