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. A simple string-based template is used to format the row expansion area.

This example is designed to assume that expanded rows will be wiped out and restored when the DataTable paginates or sorts, which is why we keep track of expanded rows and automatically restore them after each rendering of the DataTable. Anytime the restoreExpandedRows method is called and the state shows a row is open, its expansion will be re-created.

Interestingness
Loading...

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:

Also available here.

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

Copyright © 2010 Yahoo! Inc. All rights reserved.

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