YUI Library Home

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.

 
label-0
 
label-1
 
label-2
 
label-3

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    YAHOO.widget.TaskNode.superclass.constructor.call(this,oData,oParent,expanded); 
18    this.setUpCheck(checked || oData.checked); 
19 
20}; 
21 
22YAHOO.extend(YAHOO.widget.TaskNode, YAHOO.widget.TextNode, { 
23 
24    /**
25     * True if checkstate is 1 (some children checked) or 2 (all children checked),
26     * false if 0.
27     * @type boolean
28     */ 
29    checked: false
30 
31    /**
32     * checkState
33     * 0=unchecked, 1=some children checked, 2=all children checked
34     * @type int
35     */ 
36    checkState: 0, 
37 
38    /**
39     * The node type
40     * @property _type
41     * @private
42     * @type string
43     * @default "TextNode"
44     */ 
45    _type: "TaskNode"
46     
47    taskNodeParentChange: function() { 
48        //this.updateParent(); 
49    }, 
50     
51    setUpCheck: function(checked) { 
52        // if this node is checked by default, run the check code to update 
53        // the parent's display state 
54        if (checked && checked === true) { 
55            this.check(); 
56        // otherwise the parent needs to be updated only if its checkstate  
57        // needs to change from fully selected to partially selected 
58        } else if (this.parent && 2 === this.parent.checkState) { 
59             this.updateParent(); 
60        } 
61 
62        // set up the custom event on the tree for checkClick 
63        /**
64         * Custom event that is fired when the check box is clicked.  The
65         * custom event is defined on the tree instance, so there is a single
66         * event that handles all nodes in the tree.  The node clicked is 
67         * provided as an argument.  Note, your custom node implentation can
68         * implement its own node specific events this way.
69         *
70         * @event checkClick
71         * @for YAHOO.widget.TreeView
72         * @param {YAHOO.widget.Node} node the node clicked
73         */ 
74        if (this.tree && !this.tree.hasEvent("checkClick")) { 
75            this.tree.createEvent("checkClick"this.tree); 
76        } 
77 
78        this.tree.subscribe('clickEvent',this.checkClick); 
79        this.subscribe("parentChange"this.taskNodeParentChange); 
80 
81 
82    }, 
83 
84    /**
85     * The id of the check element
86     * @for YAHOO.widget.TaskNode
87     * @type string
88     */ 
89    getCheckElId: function() {  
90        return "ygtvcheck" + this.index;  
91    }, 
92 
93    /**
94     * Returns the check box element
95     * @return the check html element (img)
96     */ 
97    getCheckEl: function() {  
98        return document.getElementById(this.getCheckElId());  
99    }, 
100 
101    /**
102     * The style of the check element, derived from its current state
103     * @return {string} the css style for the current check state
104     */ 
105    getCheckStyle: function() {  
106        return "ygtvcheck" + this.checkState; 
107    }, 
108 
109 
110   /**
111     * Invoked when the user clicks the check box
112     */ 
113    checkClick: function(oArgs) {  
114        var node = oArgs.node; 
115        var target = YAHOO.util.Event.getTarget(oArgs.event); 
116        if (YAHOO.util.Dom.hasClass(target,'ygtvspacer')) { 
117            node.logger.log("previous checkstate: " + node.checkState); 
118            if (node.checkState === 0) { 
119                node.check(); 
120            } else { 
121                node.uncheck(); 
122            } 
123 
124            node.onCheckClick(node); 
125            this.fireEvent("checkClick", node); 
126            return false
127        } 
128    }, 
129 
130    /**
131     * Override to get the check click event
132     */ 
133    onCheckClick: function() {  
134        this.logger.log("onCheckClick: " + this); 
135    }, 
136 
137    /**
138     * Refresh the state of this node's parent, and cascade up.
139     */ 
140    updateParent: function() {  
141        var p = this.parent; 
142 
143        if (!p || !p.updateParent) { 
144            this.logger.log("Abort udpate parent: " + this.index); 
145            return
146        } 
147 
148        var somethingChecked = false
149        var somethingNotChecked = false
150 
151        for (var i=0, l=p.children.length;i<l;i=i+1) { 
152 
153            var n = p.children[i]; 
154 
155            if ("checked" in n) { 
156                if (n.checked) { 
157                    somethingChecked = true
158                    // checkState will be 1 if the child node has unchecked children 
159                    if (n.checkState === 1) { 
160                        somethingNotChecked = true
161                    } 
162                } else { 
163                    somethingNotChecked = true
164                } 
165            } 
166        } 
167 
168        if (somethingChecked) { 
169            p.setCheckState( (somethingNotChecked) ? 1 : 2 ); 
170        } else { 
171            p.setCheckState(0); 
172        } 
173 
174        p.updateCheckHtml(); 
175        p.updateParent(); 
176    }, 
177 
178    /**
179     * If the node has been rendered, update the html to reflect the current
180     * state of the node.
181     */ 
182    updateCheckHtml: function() {  
183        if (this.parent && this.parent.childrenRendered) { 
184            this.getCheckEl().className = this.getCheckStyle(); 
185        } 
186    }, 
187 
188    /**
189     * Updates the state.  The checked property is true if the state is 1 or 2
190     * 
191     * @param the new check state
192     */ 
193    setCheckState: function(state) {  
194        this.checkState = state; 
195        this.checked = (state > 0); 
196    }, 
197 
198    /**
199     * Check this node
200     */ 
201    check: function() {  
202        this.logger.log("check"); 
203        this.setCheckState(2); 
204        for (var i=0, l=this.children.length; i<l; i=i+1) { 
205            var c = this.children[i]; 
206            if (c.check) { 
207                c.check(); 
208            } 
209        } 
210        this.updateCheckHtml(); 
211        this.updateParent(); 
212    }, 
213 
214    /**
215     * Uncheck this node
216     */ 
217    uncheck: function() {  
218        this.setCheckState(0); 
219        for (var i=0, l=this.children.length; i<l; i=i+1) { 
220            var c = this.children[i]; 
221            if (c.uncheck) { 
222                c.uncheck(); 
223            } 
224        } 
225        this.updateCheckHtml(); 
226        this.updateParent(); 
227    }, 
228    // Overrides YAHOO.widget.TextNode 
229 
230    getContentHtml: function() {                                                                                                                                            
231        var sb = [];                                                                                                                                                        
232        sb[sb.length] = '<td';                                                                                                                                              
233        sb[sb.length] = ' id="' + this.getCheckElId() + '"';                                                                                                                
234        sb[sb.length] = ' class="' + this.getCheckStyle() + '"';                                                                                                            
235        sb[sb.length] = '>';                                                                                                                                                
236        sb[sb.length] = '<div class="ygtvspacer"></div></td>';                                                                                                              
237                                                                                                                                                                            
238        sb[sb.length] = '<td><span';                                                                                                                                        
239        sb[sb.length] = ' id="' + this.labelElId + '"';                                                                                                                     
240        if (this.title) {                                                                                                                                                   
241            sb[sb.length] = ' title="' + this.title + '"';                                                                                                                  
242        }                                                                                                                                                                   
243        sb[sb.length] = ' class="' + this.labelStyle  + '"';                                                                                                                
244        sb[sb.length] = ' >';                                                                                                                                               
245        sb[sb.length] = this.label;                                                                                                                                         
246        sb[sb.length] = '</span></td>';                                                                                                                                     
247        return sb.join("");                                                                                                                                                 
248    }   
249}); 
view plain | print | ?

Configuration for This Example

You can load the necessary JavaScript and CSS for this example from Yahoo's servers. Click here to load the YUI Dependency Configurator with all of this example's dependencies preconfigured.

Copyright © 2009 Yahoo! Inc. All rights reserved.

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