YUI Library Home

YUI Library Examples: DataTable Control: Row Expansion

DataTable Control: Row Expansion

A demonstration of the DataTable's row expansion feature to display a list of "Interestingness" from Flickr. When the table first loads, it displays a list of image titles. When a row is expanded the photo is displayed in the expansion area along with a link to the Flickr user's photostream. This example uses a simple string-based template to format the row expansion area.

Interestingness

Sample Code for this Example

Custom CSS for this example:

1/** 
2*
3* Style the yui-dt-expandablerow-trigger column 
4*
5**/ 
6.yui-dt-expandablerow-trigger{ 
7    width:18px
8    height:22px
9    cursor:pointer
10} 
11.yui-dt-expanded .yui-dt-expandablerow-trigger{ 
12    background:url(arrow_open.png) 4px 4px no-repeat
13} 
14.yui-dt-expandablerow-trigger, .yui-dt-collapsed .yui-dt-expandablerow-trigger{ 
15    background:url(arrow_closed.png) 4px 4px no-repeat
16} 
17.yui-dt-expanded .yui-dt-expandablerow-trigger.spinner{ 
18    background:url(spinner.gif) 1px 4px no-repeat
19} 
20 
21/** 
22*
23* Style the expansion row
24*
25**/ 
26.yui-dt-expansion .yui-dt-liner{ 
27    padding:0
28    border:solid 0 #bbb; 
29    border-width0 0 2px 0
30} 
31.yui-dt-expansion .yui-dt-liner th, .yui-dt-expansion .yui-dt-liner table{ 
32    border:none
33    background-color:#fff; 
34} 
35.yui-dt-expansion .yui-dt-liner th, .yui-dt-expansion .yui-dt-liner table th{ 
36    background-image:none
37    background-color:#eee; 
38} 
39.yui-dt-expansion .yui-dt-liner th, .yui-dt-expansion .yui-dt-liner table td{ 
40    border:solid 0 #eee; 
41    border-width0 0 1px 1px
42} 
43.yui-dt-expansion .yui-dt-liner th, .yui-dt-expansion .yui-dt-liner table td div{ 
44    padding:3px
45    overflow:hidden
46    width:100px
47} 
48.yui-dt-expansion .yui-dt-liner th, .yui-dt-expansion .yui-dt-liner table td.big div{ 
49    width:300px
50} 
51.yui-dt-expansion .yui-dt-liner th, .yui-dt-expansion .yui-dt-liner table td ul{ padding:0;margin:0} 
view plain | print | ?

DataTable RowExpansion extension code:

1/* This code should not be modified */ 
2 
3(function(){ 
4 
5var Dom = YAHOO.util.Dom, 
6 
7    STRING_STATENAME  = 'yui_dt_state'
8 
9    CLASS_EXPANDED    = 'yui-dt-expanded'
10    CLASS_COLLAPSED   = 'yui-dt-collapsed'
11    CLASS_EXPANSION   = 'yui-dt-expansion'
12    CLASS_LINER       = 'yui-dt-liner'
13 
14    //From YUI 3 
15    indexOf = function(a, val) { 
16        for (var i=0; i<a.length; i=i+1) { 
17            if (a[i] === val) { 
18                return i; 
19            } 
20        } 
21 
22        return -1; 
23    }; 
24 
25YAHOO.lang.augmentObject( 
26        YAHOO.widget.DataTable.prototype , { 
27 
28        //////////////////////////////////////////////////////////////////// 
29        // 
30        // Private members 
31        // 
32        //////////////////////////////////////////////////////////////////// 
33 
34        /**
35            * Gets state object for a specific record associated with the
36            * DataTable.
37            * @method _getRecordState
38            * @param {Mixed} record_id Record / Row / or Index id
39            * @param {String} key Key to return within the state object.
40            * Default is to return all as a map
41            * @return {Object} State data object
42            * @type mixed
43            * @private
44        **/ 
45        _getRecordState : function( record_id, key ){ 
46            var row_data    = this.getRecord( record_id ), 
47                row_state   = row_data.getData( STRING_STATENAME ), 
48                state_data  = (row_state && key) ? row_state[ key ]:row_state; 
49 
50            return state_data || {}; 
51        }, 
52 
53        /**
54            * Sets a value to a state object with a unique id for a record
55            * which is associated with the DataTable
56            * @method _setRecordState
57            * @param {Mixed} record_id Record / Row / or Index id
58            * @param {String} key Key to use in map
59            * @param {Mixed} value Value to assign to the key
60            * @return {Object} State data object
61            * @type mixed
62            * @private
63        **/ 
64        _setRecordState : function( record_id, key, value ){ 
65            var row_data      = this.getRecord( record_id ).getData(), 
66                merged_data   = row_data[ STRING_STATENAME ] || {}; 
67 
68            merged_data[ key ] = value; 
69 
70            this.getRecord(record_id).setData(STRING_STATENAME, merged_data); 
71 
72            return merged_data; 
73 
74        }, 
75 
76        ////////////////////////////////////////////////////////////////////// 
77        // 
78        // Public methods 
79        // 
80        ////////////////////////////////////////////////////////////////////// 
81 
82        /**
83            * Over-ridden initAttributes method from DataTable
84            * @method initAttributes
85            * @param {Mixed} record_id Record / Row / or Index id
86            * @param {String} key Key to use in map
87            * @param {Mixed} value Value to assign to the key
88            * @return {Object} State data object
89            * @type mixed
90        **/ 
91        initAttributes : function( oConfigs ) { 
92 
93            oConfigs = oConfigs || {}; 
94 
95            YAHOO.widget.DataTable.superclass.initAttributes.call(this
96                oConfigs); 
97 
98                /**
99                * @attribute rowExpansionTemplate
100                * @description Value for the rowExpansionTemplate attribute.
101                * @type {Mixed}
102                * @default ""
103                **/ 
104                this.setAttributeConfig("rowExpansionTemplate", { 
105                        value: ""
106                        validator: function( template ){ 
107                    return ( 
108                        YAHOO.lang.isString( template ) || 
109                        YAHOO.lang.isFunction( template ) 
110                    ); 
111                }, 
112                method: this.initRowExpansion 
113                }); 
114 
115        }, 
116 
117        /**
118            * Initializes row expansion on the DataTable instance
119            * @method initRowExpansion
120            * @param {Mixed} template a string template or function to be
121            * called when Row is expanded
122            * @type mixed
123        **/ 
124        initRowExpansion : function( template ){ 
125            //Set subscribe restore method 
126            this.subscribe('postRenderEvent'
127                this.onEventRestoreRowExpansion); 
128 
129            //Setup template 
130            this.rowExpansionTemplate = template; 
131 
132            //Set table level state 
133            this.a_rowExpansions = []; 
134        }, 
135 
136        /**
137            * Toggles the expansion state of a row
138            * @method toggleRowExpansion
139            * @param {Mixed} record_id Record / Row / or Index id
140            * @type mixed
141        **/ 
142        toggleRowExpansion : function( record_id ){ 
143            var state = this._getRecordState( record_id ); 
144             
145            if( state && state.expanded ){ 
146                this.collapseRow( record_id ); 
147            } else { 
148                this.expandRow( record_id ); 
149            } 
150        }, 
151 
152        /**
153            * Sets the expansion state of a row to expanded
154            * @method expandRow
155            * @param {Mixed} record_id Record / Row / or Index id
156            * @param {Boolean} restore will restore an exisiting state for a
157            * row that has been collapsed by a non user action
158            * @return {Boolean} successful
159            * @type mixed
160        **/ 
161        expandRow : function( record_id, restore ){ 
162 
163            var state = this._getRecordState( record_id ); 
164 
165            if( !state.expanded || restore ){ 
166 
167                var row_data          = this.getRecord( record_id ), 
168                    row               = this.getRow( row_data ), 
169                    new_row           = document.createElement('tr'), 
170                    column_length     = this.getFirstTrEl().childNodes.length, 
171                    expanded_data     = row_data.getData(), 
172                    expanded_content  = null
173                    template          = this.rowExpansionTemplate, 
174                    next_sibling      = Dom.getNextSibling( row ); 
175 
176                //Construct expanded row body 
177                new_row.className = CLASS_EXPANSION; 
178                var new_column = document.createElement( 'td' ); 
179                new_column.colSpan = column_length; 
180 
181                new_column.innerHTML = '<div class="'+CLASS_LINER+'"></div>'
182                new_row.appendChild( new_column ); 
183 
184                var liner_element = new_row.firstChild.firstChild; 
185 
186                if( YAHOO.lang.isString( template ) ){ 
187 
188                    liner_element.innerHTML = YAHOO.lang.substitute(  
189                        template,  
190                        expanded_data 
191                    ); 
192 
193                } else if( YAHOO.lang.isFunction( template ) ) { 
194 
195                    template( { 
196                        row_element : new_row, 
197                        liner_element : liner_element, 
198                        data : row_data,  
199                        state : state  
200 
201                    } ); 
202 
203                } else { 
204                    return false
205                } 
206 
207                //Insert new row 
208                newRow = Dom.insertAfter( new_row, row ); 
209 
210                if (newRow.innerHTML.length) { 
211 
212                    this._setRecordState( record_id, 'expanded'true ); 
213 
214                    if( !restore ){ 
215                        this.a_rowExpansions.push( 
216                            this.getRecord(record_id).getId() 
217                        ); 
218                    } 
219 
220                    Dom.removeClass( row, CLASS_COLLAPSED ); 
221                    Dom.addClass( row, CLASS_EXPANDED ); 
222 
223                    //Fire custom event 
224                    this.fireEvent( "rowExpandEvent"
225                        { record_id : row_data.getId() } ); 
226 
227                    return true
228                } else { 
229                    return false
230                }  
231            } 
232        }, 
233 
234        /**
235            * Sets the expansion state of a row to collapsed
236            * @method collapseRow
237            * @param {Mixed} record_id Record / Row / or Index id
238            * @return {Boolean} successful
239            * @type mixed
240        **/ 
241        collapseRow : function( record_id ){ 
242            var row_data    = this.getRecord( record_id ), 
243                row         = Dom.get( row_data.getId() ), 
244                state       = row_data.getData( STRING_STATENAME ); 
245 
246            if( state && state.expanded ){ 
247                var next_sibling = Dom.getNextSibling( row ), 
248                        hash_index = indexOf(this.a_rowExpansions, record_id); 
249 
250                if( Dom.hasClass( next_sibling, CLASS_EXPANSION ) ) { 
251                    next_sibling.parentNode.removeChild( next_sibling ); 
252                    this.a_rowExpansions.splice( hash_index, 1 ); 
253                    this._setRecordState( record_id, 'expanded'false ); 
254 
255                    Dom.addClass( row, CLASS_COLLAPSED ); 
256                    Dom.removeClass( row, CLASS_EXPANDED ); 
257 
258                    //Fire custom event 
259                    this.fireEvent("rowCollapseEvent"
260                        {record_id:row_data.getId()}); 
261 
262                    return true
263                } else { 
264                    return false
265                } 
266            } 
267        }, 
268 
269        /**
270            * Collapses all expanded rows. This should be called before any
271            * action where the row expansion markup would interfere with
272            * normal DataTable markup handling. This method does not remove
273            * events attached during implementation. All event handlers should
274            * be removed separately.
275            * @method collapseAllRows
276            * @type mixed
277        **/ 
278        collapseAllRows : function(){ 
279            var rows = this.a_rowExpansions; 
280 
281            forvar i = 0, l = rows.length; l > i; i++ ){ 
282                //Always pass 0 since collapseRow 
283                //removes item from the a_rowExpansions array 
284                this.collapseRow( rows[ 0 ] ); 
285 
286            } 
287 
288            a_rowExpansions = []; 
289 
290        }, 
291 
292        /**
293            * Restores rows which have an expanded state but no markup. This
294            * is to be called to restore row expansions after the DataTable
295            * renders or the collapseAllRows is called.
296            * @method collapseAllRows
297            * @type mixed
298        **/ 
299        restoreExpandedRows : function(){ 
300            var expanded_rows = this.a_rowExpansions; 
301             
302            if( !expanded_rows.length ){ 
303                return
304            } 
305 
306            ifthis.a_rowExpansions.length ){ 
307                forvar i = 0, l = expanded_rows.length; l > i; i++ ){ 
308                    this.expandRow( expanded_rows[ i ] , true ); 
309                } 
310            } 
311        }, 
312 
313        /**
314            * Abstract method which restores row expansion for subscribing to
315            * the DataTable postRenderEvent.
316            * @method onEventRestoreRowExpansion
317            * @param {Object} oArgs context of a subscribed event
318            * @type mixed
319        **/ 
320        onEventRestoreRowExpansion : function( oArgs ){ 
321            this.restoreExpandedRows(); 
322        }, 
323 
324        /**
325            * Abstract method which toggles row expansion for subscribing to
326            * the DataTable postRenderEvent.
327            * @method onEventToggleRowExpansion
328            * @param {Object} oArgs context of a subscribed event
329            * @type mixed
330        **/ 
331        onEventToggleRowExpansion : function( oArgs ){ 
332            if(YAHOO.util.Dom.hasClass(oArgs.target, 
333                'yui-dt-expandablerow-trigger')){ 
334                this.toggleRowExpansion( oArgs.target ); 
335            } 
336        } 
337 
338    }, true //This boolean needed to override members of the original object 
339); 
340 
341})(); 
view plain | print | ?

JavaScript to run this example:

1/* Modify as needed */ 
2 
3YAHOO.util.Event.onDOMReady( function() { 
4    YAHOO.example.Basic = function() { 
5        var expansionFormatter  = function(el, oRecord, oColumn, oData) { 
6            var cell_element    = el.parentNode; 
7 
8            //Set trigger 
9            if( oData ){ //Row is closed 
10                YAHOO.util.Dom.addClass( cell_element, 
11                    "yui-dt-expandablerow-trigger" ); 
12            } 
13 
14        }; 
15 
16        var myDataSource = new 
17            YAHOO.util.DataSource(YAHOO.example.interestingness); 
18            myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY; 
19            myDataSource.responseSchema = { 
20                fields: ["title","farm","server","id","secret","owner"
21            }; 
22 
23        var myDataTable = new YAHOO.widget.DataTable( 
24                "event_table"
25                [ 
26                    { 
27                        key:"farm"
28                        label:""
29                        formatter:expansionFormatter 
30                    }, 
31                    { 
32                        key:"title"
33                        label:"Interestingness"
34                        width : '200px'
35                        sortable:true 
36                    } 
37                ], 
38                myDataSource, 
39                    { rowExpansionTemplate : 
40                    '<img src="http://farm{farm}.static.flickr.com/'
41                    '{server}/{id}_{secret}_m_d.jpg" />' } 
42                ); 
43 
44        //Subscribe to a click event to bind to 
45        myDataTable.subscribe( 'cellClickEvent'
46            myDataTable.onEventToggleRowExpansion ); 
47         
48        return { 
49            oDS: myDataSource, 
50            oDT: myDataTable 
51        }; 
52    }(); 
53}); 
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.

YUI Logger Output:

Logger Console

INFO 162ms (+162) 12:25:36 AM:

LogReader instance0

LogReader initialized

INFO 0ms (+0) 12:25:36 AM:

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 DataTable Control Resources:

Copyright © 2009 Yahoo! Inc. All rights reserved.

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