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.

Data error.

Custom CSS for this Example

1/** 
2*
3* Style the yui-dt-expandablerow-trigger column 
4*
5**/ 
6.yui-dt-expandablerow-trigger a { 
7    display:block
8    padding:20px 5px 0
9    cursor:pointer
10} 
11.yui-dt-expanded .yui-dt-expandablerow-trigger a{ 
12    background:url(arrow_open.png) 0 6px no-repeat
13} 
14.yui-dt-expandablerow-trigger a, .yui-dt-collapsed .yui-dt-expandablerow-trigger a { 
15    background:url(arrow_closed.png) 0 4px no-repeat
16} 
17.yui-dt-expanded .yui-dt-expandablerow-trigger.spinner a { 
18    background:url(spinner.gif) 0 4px no-repeat
19    padding-right10px
20} 
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             * Override to work with pagination.
102             *
103             * @method onPaginatorChangeRequest
104             * @private
105             */ 
106            onPaginatorChangeRequest : function() { 
107 
108                this.collapseAllRows(); 
109                YAHOO.widget.RowExpansionDataTable.superclass.onPaginatorChangeRequest.apply(this, arguments); 
110 
111            }, 
112 
113            /**
114             * Override to work with pagination.
115             *
116             * @method doBeforeSortColumn
117             * @private
118             */ 
119            sortColumn : function() { 
120 
121                this.collapseAllRows(); 
122                YAHOO.widget.RowExpansionDataTable.superclass.sortColumn.apply(this, arguments); 
123 
124            }, 
125 
126            /**
127             * Override to work around IE6 issue.
128             *
129             * @method _setColumnWidth
130             * @private
131             */ 
132            _setColumnWidth : function() { 
133 
134                var save; 
135                if (this.a_rowExpansions && YAHOO.widget.DataTable._bDynStylesFallback) { 
136                    save = this.a_rowExpansions.slice(0); 
137                    this.collapseAllRows(); 
138                } 
139 
140                YAHOO.widget.RowExpansionDataTable.superclass._setColumnWidth.apply(this, arguments); 
141 
142                if (save) { 
143                    this.a_rowExpansions = save; 
144                    this.restoreExpandedRows(); 
145                } 
146 
147            }, 
148 
149            ////////////////////////////////////////////////////////////////// 
150            // 
151            // Public methods 
152            // 
153            ////////////////////////////////////////////////////////////////// 
154 
155            /**
156             * Over-ridden initAttributes method from DataTable
157             *
158             * @method initAttributes
159             * @param {Mixed} record_id Record / Row / or Index id
160             * @param {String} key Key to use in map
161             * @param {Mixed} value Value to assign to the key
162             * @return {Object} State data object
163            **/ 
164            initAttributes : function( oConfigs ) { 
165 
166                oConfigs = oConfigs || {}; 
167 
168                YAHOO.widget.RowExpansionDataTable.superclass.initAttributes.call( this, oConfigs ); 
169 
170                /**
171                 * The rowExpansionTemplate attribute can accept a string
172                 * template or a function. The function will receive an argument
173                 * "o" with the following properties:
174                 *      o.data // Record instance for the expanded row
175                 *      o.liner_element // The liner DIV element of the expansion
176                 *      o.row_element // The TR element of the expansion
177                 *      o.state // The DataTable state object
178                 *
179                 * @attribute rowExpansionTemplate
180                 * @type {Mixed}
181                 * @default ""
182                **/ 
183                this.setAttributeConfig("rowExpansionTemplate", { 
184                    value: ""
185                    validator: function( template ){ 
186                        return ( 
187                            YAHOO.lang.isString( template ) || 
188                            YAHOO.lang.isFunction( template ) 
189                        ); 
190                    }, 
191                    method: this.initRowExpansion 
192                }); 
193 
194            }, 
195 
196            /**
197             * Initializes row expansion on the DataTable instance
198             *
199             * @method initRowExpansion
200             * @param {Mixed} template a string template or function to be
201             *                         called when Row is expanded
202            **/ 
203            initRowExpansion : function( template ){ 
204 
205                //Set subscribe restore method 
206                this.subscribe( 'postRenderEvent'this.onEventRestoreRowExpansion ); 
207 
208                //Setup template 
209                this.rowExpansionTemplate = template; 
210 
211                //Set table level state 
212                this.a_rowExpansions = []; 
213 
214            }, 
215 
216            /**
217             * Toggles the expansion state of a row
218             *
219             * @method toggleRowExpansion
220             * @param {Mixed} record_id Record / Row / or Index id
221            **/ 
222            toggleRowExpansion : function( record_id ){ 
223 
224                var state = this._getRecordState( record_id ); 
225 
226                if( state && state.expanded ){ 
227 
228                    this.collapseRow( record_id ); 
229 
230                } else { 
231 
232                    this.expandRow( record_id ); 
233 
234                } 
235 
236            }, 
237 
238            /**
239             * Sets the expansion state of a row to expanded
240             *
241             * @method expandRow
242             * @param {Mixed} record_id Record / Row / or Index id
243             * @param {Boolean} restore will restore an exisiting state for a
244             * row that has been collapsed by a non user action
245             * @return {Boolean} successful
246            **/ 
247            expandRow : function( record_id, restore ){ 
248 
249                var state = this._getRecordState( record_id ); 
250 
251                if( !state.expanded || restore ){ 
252 
253                    var row_data          = this.getRecord( record_id ), 
254                        row               = this.getRow( row_data ), 
255                        new_row           = document.createElement('tr'), 
256                        column_length     = this.getFirstTrEl().childNodes.length, 
257                        expanded_data     = row_data.getData(), 
258                        expanded_content  = null
259                        template          = this.rowExpansionTemplate, 
260                        next_sibling      = Dom.getNextSibling( row ); 
261 
262                    //Construct expanded row body 
263                    new_row.className = CLASS_EXPANSION; 
264                    var new_column = document.createElement( 'td' ); 
265                    new_column.colSpan = column_length; 
266 
267                    new_column.innerHTML = '<div class="'+ CLASS_LINER +'"></div>'
268                    new_row.appendChild( new_column ); 
269 
270                    var liner_element = new_row.firstChild.firstChild; 
271 
272                    if( YAHOO.lang.isString( template ) ){ 
273 
274                        liner_element.innerHTML = YAHOO.lang.substitute(  
275                            template,  
276                            expanded_data 
277                        ); 
278 
279                    } else if( YAHOO.lang.isFunction( template ) ) { 
280 
281                        template( { 
282                            row_element : new_row, 
283                            liner_element : liner_element, 
284                            data : row_data,  
285                            state : state  
286                        } ); 
287 
288                    } else { 
289 
290                        return false
291 
292                    } 
293 
294                    //Insert new row 
295                    newRow = Dom.insertAfter( new_row, row ); 
296 
297                    if (newRow.innerHTML.length) { 
298 
299                        this._setRecordState( record_id, 'expanded'true ); 
300 
301                        if( !restore ){ 
302 
303                            this.a_rowExpansions.push( this.getRecord( record_id ).getId() ); 
304 
305                        } 
306 
307                        Dom.removeClass( row, CLASS_COLLAPSED ); 
308                        Dom.addClass( row, CLASS_EXPANDED ); 
309 
310                        //Fire custom event 
311                        this.fireEvent( "rowExpandEvent", { record_id : row_data.getId() } ); 
312 
313                        return true
314 
315                    } else { 
316 
317                        return false
318 
319                    }  
320 
321                } 
322 
323            }, 
324 
325            /**
326             * Sets the expansion state of a row to collapsed
327             * @method collapseRow
328             * @param {Mixed} record_id Record / Row / or Index id
329             * @return {Boolean} successful
330            **/ 
331            collapseRow : function( record_id ){ 
332 
333                var row_data = this.getRecord( record_id ), 
334                    row      = Dom.get( row_data.getId() ), 
335                    state    = row_data.getData( STRING_STATENAME ); 
336 
337                if( state && state.expanded ){ 
338 
339                    var next_sibling = Dom.getNextSibling( row ), 
340                        hash_index   = indexOf( this.a_rowExpansions, record_id ); 
341 
342                    if( Dom.hasClass( next_sibling, CLASS_EXPANSION ) ) { 
343 
344                        next_sibling.parentNode.removeChild( next_sibling ); 
345                        this.a_rowExpansions.splice( hash_index, 1 ); 
346                        this._setRecordState( record_id, 'expanded'false ); 
347 
348                        Dom.addClass( row, CLASS_COLLAPSED ); 
349                        Dom.removeClass( row, CLASS_EXPANDED ); 
350 
351                        //Fire custom event 
352                        this.fireEvent("rowCollapseEvent", { record_id : row_data.getId() } ); 
353 
354                        return true
355 
356                    } else { 
357 
358                        return false
359 
360                    } 
361 
362                } 
363 
364            }, 
365 
366            /**
367             * Collapses all expanded rows. This should be called before any
368             * action where the row expansion markup would interfear with
369             * normal DataTable markup handling. This method does not remove
370             * exents attached during implementation. All event handlers should
371             * be removed separately.
372             *
373             * @method collapseAllRows
374            **/ 
375            collapseAllRows : function(){ 
376 
377                var rows = this.a_rowExpansions; 
378 
379                forvar i = 0, l = rows.length; l > i; i++ ){ 
380 
381                    //Always pass 0 since collapseRow removes item from the a_rowExpansions array 
382                    this.collapseRow( rows[ 0 ] ); 
383 
384                } 
385 
386                this.a_rowExpansions = []; 
387 
388            }, 
389 
390            /**
391             * Restores rows which have an expanded state but no markup. This
392             * is to be called to restore row expansions after the DataTable
393             * renders or the collapseAllRows is called.
394             *
395             * @method collapseAllRows
396            **/ 
397            restoreExpandedRows : function(){ 
398 
399                var expanded_rows = this.a_rowExpansions; 
400 
401                if( !expanded_rows.length ){ 
402 
403                    return
404 
405                } 
406 
407                ifthis.a_rowExpansions.length ){ 
408 
409                    forvar i = 0, l = expanded_rows.length; l > i; i++ ){ 
410 
411                        this.expandRow( expanded_rows[ i ] , true ); 
412 
413                    } 
414 
415                } 
416 
417            }, 
418 
419            /**
420             * Abstract method which restores row expansion for subscribing to
421             * the DataTable postRenderEvent.
422             *
423             * @method onEventRestoreRowExpansion
424             * @param {Object} oArgs context of a subscribed event
425            **/ 
426            onEventRestoreRowExpansion : function( oArgs ){ 
427 
428                this.restoreExpandedRows(); 
429 
430            }, 
431 
432            /**
433             * Abstract method which toggles row expansion for subscribing to
434             * the DataTable postRenderEvent.
435             *
436             * @method onEventToggleRowExpansion
437             * @param {Object} oArgs context of a subscribed event
438            **/ 
439            onEventToggleRowExpansion : function( oArgs ){ 
440 
441                if( YAHOO.util.Dom.hasClass( oArgs.target, 'yui-dt-expandablerow-trigger' ) ){ 
442 
443                    this.toggleRowExpansion( oArgs.target ); 
444 
445                } 
446 
447            } 
448 
449        }); 
450 
451})(); 
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 © 2011 Yahoo! Inc. All rights reserved.

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