YUI recommends YUI3.

YUI 2 has been deprecated since 2011. This site acts as an archive for files and documentation.

This documentation is no longer maintained.

YUI 2: DataTable

YUI 2: DataTable

The DataTable Control interface with the default YUI Sam Skin applied.The DataTable control provides a simple yet powerful API to display screen-reader accessible tabular data on a web page. Notable features include sortable columns, pagination, scrolling, row selection, resizeable columns, and inline cell editing.

YUI community member Daniel Barreiro (screen name Satyam) has contributed two terrific articles on the YUI DataTable Control on YUIBlog:

In addition to reading this User's Guide and the canonical DataTable examples by DataTable author Jenny Han Donnelly, we strongly recommend that you review Satyam's work in preparing for your DataTable implementation.

Note: The 2.6.0 release of DataTable introduces the ScrollingDataTable and CellEditor classes, the Paginator widget is now offered as a standalone component, and significant changes have been applied to DataTable's pagination and sorting architectures. While backward compatibility has been maintained whenever possible, the Paginator widget is now an optional dependency, and custom extensions to inline cell editing or server-side pagination and/or sorting will likely need to be updated to be compatible with the new models. Implementers who are upgrading from an earlier version are strongly advised to read the Upgrades Notes.

Upgrade Notes

Users new to DataTable can skip this section and proceed directly to the Getting Started section. Implementers who are upgrading from previous versions should note the following changes for version 2.6.0:

  • Paginator has been extracted as a standalone class and optional dependency.
  • Functionality for xy-scrollability has been ported to YAHOO.widget.ScrollingDataTable, a subclass of YAHOO.widget.DataTable. Non-scrolling DataTables will no longer generate any extraneous markup that is an artifact of the scrolling architecture. Only ScrollingDataTables will generate the additional markup needed to support xy-scrolling. Implementers should instantiate a ScrollingDataTable directly by calling new YAHOO.widget.ScrollingDataTable. For backward compatibility, a ScrollingDataTable instance will also be returned if {scrollable:true} is passed to the constructor for DataTable.
  • Inline cell editing functionality has been re-implemented as YAHOO.widget.CellEditor and related subclasses. The following methods are no longer supported:
    • DataTable.editCheckbox() has been removed. The CheckboxCellEditor class should be used instead.
    • DataTable.editDate() has been removed. The DateCellEditor class should be used instead.
    • DataTable.editDropdown() has been removed. The DropdownCellEditor class should be used instead.
    • DataTable.editRadio() has been removed. The RadioCellEditor class should be used instead.
    • DataTable.editTextarea() has been removed. The TextareaCellEditor class should be used instead.
    • DataTable.editTextbox() has been removed. The TextboxCellEditor class should be used instead.
    • showCellEditorBtns() has been removed. The CellEditor method renderBtns() should be used instead.
    • editorUpdateEvent has been removed.
    • resetCellEditor() has been renamed to destroyCellEditor().
  • The values for CellEditor checkboxOptions, dropdownOptions, and radioOptions must be either a simple Array or an Array of object literals with properties "value" and "label". Backward compatibility is not being supported in this area, so please update your implementations as needed.
  • A new CellEditor property asyncSubmitter can be used to submit input values and will block the DataTable UI (via new DataTable methods disable() and undisable()) until the callback function is executed to finish the transaction.
  • The CellEditor's "Save" and "Cancel" buttons now have configurable labels.
  • CellEditor validator functions, including the built-in function YAHOO.widget.DataTable.validateNumber must return undefined for invalid values instead of null.
  • Pagination and sorting have been significantly reworked to better support dynamically driven DataTables. As a result, the following important changes have been made:
    • Removed support for all "magic meta" fields. Server-provided values, such as totalRecords will need to be explicitly updated by custom implementation code as described in the Server-side Sorting and Pagination of Dynamic Data section.
    • The following APIs have been removed:
      • paginationEventHandler Attribute
      • handleSimplePagination()
      • handleDataSourcePagination()
      • updatePaginator()
      • showPage()
      • formatPaginator()
      • formatPaginationDropdown()
      • formatPaginatorLinks()
    • The dynamicData Attribute has been added to better support server-side pagination and/or sorting.
    • The onPaginatorChange() method has been renamed to onPaginatorChangeRequest()
    • Removed backward compatibility support for the paginated Attribute and the object literal paginator Attribute value. Implementers must use the Paginator class to populate the paginator Attribute.
  • The following APIs have been changed from static class properties to instance Attributes, to be set via the initial config or myDataTable.set():
    • MSG_EMPTY
    • MSG_ERROR
    • MSG_LOADING
    • COLOR_COLUMNFILLER (moved to ScrollingDataTable class)
  • The formatTheadCell() method been changed from static a static method to an instance method with an update to its argument signature.
  • The initEvent will fire when rows are rendered from an initialized state, and the renderEvent will always fire when rows are rendered, and also when the underlying DOM incrementally changes (such as adding or deleting rows or Columns). This is a change from prior behavior, when the renderEvent would *not* fire if the initEvent was fired and only when the entire view was rendered (such as a new page load).
  • For consistency with other "doBefore" abstract methods, doBeforeShowCellEditor() now returns true by default and returns false to cancel showing the cell editor.
  • The Column property minWidth default value is now null.
  • Added Column property maxAutoWidth.
  • In the generated markup, the primary data TBODY is (once again) in the DOM before the message TBODY element.
  • Resizeable Columns now create an additional resizer liner DIV element between the TH element and the liner DIV element. Implementers are advised to access the liner DIV elements via Column method getThLinerEl() rather than directly accessing the DOM via TH.firstChild.
  • TR element IDs are now assigned with the corresponding Record ID, and the heretofore unused TD IDs have been removed.
  • All CSS classes representing Column states are now assigned directly on TH and TD elements, not on liner DIV elements.

Getting Started

To use the DataTable control, include the following source files in your web page.

  1. <!--CSS file (default YUI Sam Skin) -->
  2. <link type="text/css" rel="stylesheet" href="http://yui.yahooapis.com/2.9.0/build/datatable/assets/skins/sam/datatable.css">
  3.  
  4. <!-- Dependencies -->
  5. <script src="http://yui.yahooapis.com/2.9.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
  6. <script src="http://yui.yahooapis.com/2.9.0/build/element/element-min.js"></script>
  7. <script src="http://yui.yahooapis.com/2.9.0/build/datasource/datasource-min.js"></script>
  8.  
  9. <!-- OPTIONAL: JSON Utility (for DataSource) -->
  10. <script src="http://yui.yahooapis.com/2.9.0/build/json/json-min.js"></script>
  11.  
  12. <!-- OPTIONAL: Connection Manager (enables XHR for DataSource) -->
  13. <script src="http://yui.yahooapis.com/2.9.0/build/connection/connection-min.js"></script>
  14.  
  15. <!-- OPTIONAL: Get Utility (enables dynamic script nodes for DataSource) -->
  16. <script src="http://yui.yahooapis.com/2.9.0/build/get/get-min.js"></script>
  17.  
  18. <!-- OPTIONAL: Drag Drop (enables resizeable or reorderable columns) -->
  19. <script src="http://yui.yahooapis.com/2.9.0/build/dragdrop/dragdrop-min.js"></script>
  20.  
  21. <!-- OPTIONAL: Calendar (enables calendar editors) -->
  22. <script src="http://yui.yahooapis.com/2.9.0/build/calendar/calendar-min.js"></script>
  23.  
  24. <!-- Source files -->
  25. <script src="http://yui.yahooapis.com/2.9.0/build/datatable/datatable-min.js"></script>
  26.  
<!--CSS file (default YUI Sam Skin) -->
<link type="text/css" rel="stylesheet" href="http://yui.yahooapis.com/2.9.0/build/datatable/assets/skins/sam/datatable.css">
 
<!-- Dependencies -->
<script src="http://yui.yahooapis.com/2.9.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script src="http://yui.yahooapis.com/2.9.0/build/element/element-min.js"></script>
<script src="http://yui.yahooapis.com/2.9.0/build/datasource/datasource-min.js"></script>
 
<!-- OPTIONAL: JSON Utility (for DataSource) -->
<script src="http://yui.yahooapis.com/2.9.0/build/json/json-min.js"></script>
 
<!-- OPTIONAL: Connection Manager (enables XHR for DataSource) -->
<script src="http://yui.yahooapis.com/2.9.0/build/connection/connection-min.js"></script>
 
<!-- OPTIONAL: Get Utility (enables dynamic script nodes for DataSource) -->
<script src="http://yui.yahooapis.com/2.9.0/build/get/get-min.js"></script>
 
<!-- OPTIONAL: Drag Drop (enables resizeable or reorderable columns) -->
<script src="http://yui.yahooapis.com/2.9.0/build/dragdrop/dragdrop-min.js"></script>
 
<!-- OPTIONAL: Calendar (enables calendar editors) -->
<script src="http://yui.yahooapis.com/2.9.0/build/calendar/calendar-min.js"></script>
 
<!-- Source files -->
<script src="http://yui.yahooapis.com/2.9.0/build/datatable/datatable-min.js"></script>
 
Next, apply the yui-skin-sam class name to an element that is a parent of the element in which the DataTable Control lives. You can usually accomplish this simply by putting the class on the <body> tag:

  1. <body class="yui-skin-sam">
  2.  
<body class="yui-skin-sam">
 

For more information on skinning YUI components and making use of default skins, see our Understanding YUI Skins article here on the website.

YUI dependency configurator.

YUI Dependency Configurator:

Instead of copying and pasting the filepaths above, try letting the YUI dependency Configurator determine the optimal file list for your desired components; the Configurator uses YUI Loader to write out the full HTML for including the precise files you need for your implementation.

Note: If you wish to include this component via the YUI Loader, its module name is datatable. (Click here for the full list of module names for YUI Loader.)

Where these files come from: The files included using the text above will be served from Yahoo! servers. JavaScript files are minified, meaning that comments and white space have been removed to make them more efficient to download. To use the full, commented versions or the -debug versions of YUI JavaScript files, please download the library distribution and host the files on your own server.

Order matters: As is the case generally with JavaScript and CSS, order matters; these files should be included in the order specified above. If you include files in the wrong order, errors may result.

Object Model and DOM Overview

DataTable creates an internal ColumnSet object to define the header cells for the <thead> and an internal RecordSet object to locally hold data for the rows of the table. The ColumnSet is created using the Column definitions passed in via the constructor, and the RecordSet is created using the ColumnSet object and populated with data provided by the DataSource.

  • ColumnSet The ColumnSet is a set of Column instances. Each Column represents a field of data from the DataSource. Each Column is assigned
    • a unique key which names the Column (one is created if not provided)
    • a field which maps to a field defined in the DataSource schema
    • a key index which represents its order within the ColumnSet
    • an ID which is a globally unique identifier across all ColumnSets
  • RecordSet The RecordSet is a set of Record instances, each of which represents a row of data. Each Record is assigned
    • a globally unique ID, which remains constant regardless of sort order
    • a RecordSet index which represents its current sort order index within the RecordSet
  • DOM elements In the DOM, a <table> element contains a collection of <tr> elements. Each <tr> is assigned a Record instance to display, and its unique DOM ID is assigned its corresponding Record's ID. If the DataTable is paginated, then the <table> will show only a subset of the entire RecordSet.
  • DataTable The DataTable class manages the interaction between the ColumnSet, the RecordSet, and DOM Elements and exposes the API through which developers can build and configure their tables.
  • ScrollingDataTable The ScrollingDataTable class extends the DataTable class to add functionality specific to xy-scrolling.

The markup created for each DataTable instance starts with the implementer-provided container, into which a <table> element is create. Within the <table> element, two <tbody> elements are created: a message <tbody> element to display stateful messages such as "Loading data..." or "No data found" and also a primary <tbody> element that contains the cells of data.

In the case of a ScrollingDataTable, there are actually two <table> elements, each housed in its own container, identified as the "header container" and the "body container". The <table> in the header container consists of a <table> element with only a <thead> element of Column header <th>s, whose purpose is to remain fixed on the screen when the body cells scroll vertically. The <table> generated into the body container is nearly identical to the <table> created by the DataTable class, with the exception of the <thead> element which is placed offscreen for screenreader consumption only.

The following markup is a generic sample of the DOM elements that are generated by the DataTable class. Note that Column keys are also assigned as CSS class names on DOM elements so they can be used as hooks to skin or customize the UI. As such Column keys are subject to string validation before being used in the DOM or in CSS. Please see the Column Keys Usage section for more information.

  1. <div id="myContainer" class="yui-dt">
  2. <div class="yui-dt-mask" style="display: none;"/>
  3. <table summary="">
  4. <colgroup>
  5. <col/>
  6. <col/>
  7. <col/>
  8. <col/>
  9. <col/>
  10. </colgroup>
  11. <thead>
  12. <tr class="yui-dt-first yui-dt-last">
  13. <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-first yui-dt-asc">
  14. <div class="yui-dt-resizerliner">
  15. <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
  16. <span class="yui-dt-label">
  17. <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header A</a>
  18. </span>
  19. </div>
  20. <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
  21. </div>
  22. </th>
  23. <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  24. <div class="yui-dt-resizerliner">
  25. <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
  26. <span class="yui-dt-label">
  27. <a class="yui-dt-sortable" title="Click to sort descending" href="yui-dt0-href-{sanitizedkey}">Header B</a>
  28. </span>
  29. </div>
  30. <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
  31. </div>
  32. </th>
  33. <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  34. <div class="yui-dt-resizerliner">
  35. <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
  36. <span class="yui-dt-label">
  37. <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header C</a>
  38. </span>
  39. </div>
  40. <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
  41. </div>
  42. </th>
  43. <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  44. <div class="yui-dt-resizerliner">
  45. <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
  46. <span class="yui-dt-label">
  47. <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header D</a>
  48. </span>
  49. </div>
  50. <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
  51. </div>
  52. </th>
  53. <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-last">
  54. <div class="yui-dt-resizerliner">
  55. <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
  56. <span class="yui-dt-label">
  57. <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header E</a>
  58. </span>
  59. </div>
  60. <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
  61. </div>
  62. </th>
  63. </tr>
  64. </thead>
  65. <caption>
  66. DataTable Caption
  67. </caption>
  68. <tbody class="yui-dt-message" style="display: none;">
  69. <tr class="yui-dt-first yui-dt-last">
  70. <td colspan="5" class="yui-dt-loading">
  71. <div class="yui-dt-liner">
  72. Loading...
  73. </div>
  74. </td>
  75. </tr>
  76. </tbody>
  77. <tbody tabindex="0" class="yui-dt-data" style="">
  78. <tr style="" id="yui-rec0" class="yui-dt-first yui-dt-even">
  79. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-first yui-dt-asc">
  80. <div class="yui-dt-liner">
  81. Cell A
  82. </div>
  83. </td>
  84. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  85. <div class="yui-dt-liner">
  86. Cell B
  87. </div>
  88. </td>
  89. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  90. <div class="yui-dt-liner">
  91. Cell C
  92. </div>
  93. </td>
  94. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  95. <div class="yui-dt-liner">
  96. Cell D
  97. </div>
  98. </td>
  99. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-last">
  100. <div class="yui-dt-liner">
  101. Cell E
  102. </div>
  103. </td>
  104. </tr>
  105. ...
  106. <tr style="" id="yui-rec3" class="yui-dt-last yui-dt-odd">
  107. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-first yui-dt-asc">
  108. <div class="yui-dt-liner">
  109. Cell A
  110. </div>
  111. </td>
  112. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  113. <div class="yui-dt-liner">
  114. Cell B
  115. </div>
  116. </td>
  117. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  118. <div class="yui-dt-liner">
  119. Cell C
  120. </div>
  121. </td>
  122. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
  123. <div class="yui-dt-liner">
  124. Cell D
  125. </div>
  126. </td>
  127. <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-last">
  128. <div class="yui-dt-liner">
  129. Cell E
  130. </div>
  131. </td>
  132. </tr>
  133. </tbody>
  134. </table>
  135. </div>
  136.  
<div id="myContainer" class="yui-dt">
    <div class="yui-dt-mask" style="display: none;"/>
    <table summary="">
        <colgroup>
        <col/>
        <col/>
        <col/>
        <col/>
        <col/>
        </colgroup>
        <thead>
            <tr class="yui-dt-first yui-dt-last">
                <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-first yui-dt-asc">
                    <div class="yui-dt-resizerliner">
                        <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
                            <span class="yui-dt-label">
                                <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header A</a>
                            </span>
                        </div>
                        <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
                    </div>
                </th>
                <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-resizerliner">
                        <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
                            <span class="yui-dt-label">
                                <a class="yui-dt-sortable" title="Click to sort descending" href="yui-dt0-href-{sanitizedkey}">Header B</a>
                            </span>
                        </div>
                        <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
                    </div>
                </th>
                <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-resizerliner">
                        <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
                            <span class="yui-dt-label">
                                <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header C</a>
                            </span>
                        </div>
                        <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
                    </div>
                </th>
                <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-resizerliner">
                        <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
                            <span class="yui-dt-label">
                                <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header D</a>
                            </span>
                        </div>
                        <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
                    </div>
                </th>
                <th id="yui-dt0-th-{sanitizedkey}" rowspan="1" colspan="1" class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-last">
                    <div class="yui-dt-resizerliner">
                        <div id="yui-dt0-th-{sanitizedkey}-liner" class="yui-dt-liner">
                            <span class="yui-dt-label">
                                <a class="yui-dt-sortable" title="Click to sort ascending" href="yui-dt0-href-{sanitizedkey}">Header E</a>
                            </span>
                        </div>
                        <div id="yui-dt0-th-{sanitizedkey}-resizer" class="yui-dt-resizer" style="left: auto; right: 0pt; top: auto; bottom: 0pt; height: 24px;"/>
                    </div>
                </th>
            </tr>
        </thead>
        <caption>
            DataTable Caption
        </caption>
        <tbody class="yui-dt-message" style="display: none;">
            <tr class="yui-dt-first yui-dt-last">
                <td colspan="5" class="yui-dt-loading">
                    <div class="yui-dt-liner">
                        Loading...
                    </div>
                </td>
            </tr>
        </tbody>
        <tbody tabindex="0" class="yui-dt-data" style="">
            <tr style="" id="yui-rec0" class="yui-dt-first yui-dt-even">
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-first yui-dt-asc">
                    <div class="yui-dt-liner">
                        Cell A
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-liner">
                        Cell B
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-liner">
                        Cell C
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-liner">
                        Cell D
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-last">
                    <div class="yui-dt-liner">
                        Cell E
                    </div>
                </td>
            </tr>
            ...
            <tr style="" id="yui-rec3" class="yui-dt-last yui-dt-odd">
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-first yui-dt-asc">
                    <div class="yui-dt-liner">
                        Cell A
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-liner">
                        Cell B
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-liner">
                        Cell C
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable">
                    <div class="yui-dt-liner">
                        Cell D
                    </div>
                </td>
                <td headers="yui-dt0-th-{sanitizedkey} " class="yui-dt0-col-{sanitizedkey} yui-dt-col-{sanitizedkey} yui-dt-sortable yui-dt-resizeable yui-dt-last">
                    <div class="yui-dt-liner">
                        Cell E
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
</div>
 

Understanding Records, Rows, and DOM Elements

The DataTable API provides a set of methods for implementers to manipulate underlying data and associated DOM elements, such as addRow(), updateRow(), and deleteRow(). Calling addRow(oData) on the DataTable will add a new Record of data to the end of the RecordSet, and create and populate a new <tr> element at the bottom of the <table> element. Calling addRow(oData, i) will create a new Record of data and a new <tr> element and insert them into the given index position i of the RecordSet and <table>, respectively.

When pagination is enabled, calling addRow() will always add a Record to the RecordSet, but will only add a <tr> element to the <table> if the new Record is in view within the current page. Implementers should keep this in mind when dealing with RecordSet index values, as these values may or may not be equal to <tr> index values when pagination is enabled.

Instantiating DataTable

A DataTable is instantiated by passing in the following to the constructor:

  1. An ID string or element reference to a container DIV element to host the rendered markup
  2. A set of Column definitions
  3. A DataSource instance to manage data retrieval from a variety of sources, from local JavaScript arrays to remote online servers. Please refer to the DataSource documentation for more information on using this utility.
  1. // DataTable constructor syntax
  2. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource);
  3.  
// DataTable constructor syntax
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource);
 

The Container Element

Make sure the container element is available in the DOM before instantiating your DataTable, either by placing your script in the HTML body after the markup has been rendered, waiting until the window load DOM event fires, or by using the Event Utility's onAvailable method to programmatically create your DataTable as soon as the container element is available.

  1. // Defer instantiation
  2. YAHOO.util.Event.addListener(window, "load", function() {
  3. var myDataTableDeferred = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource);
  4. });
  5.  
// Defer instantiation
YAHOO.util.Event.addListener(window, "load", function() {
    var myDataTableDeferred = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource);
});
 

Column Definitions

The second argument of the DataTable constructor is an array of object literals to define the Columns that are rendered into the table. In the simple example below, we define our DataTable to display three Columns, which we name "fname", "lname", and "age":

  1. var myColumnDefs = [
  2. { key: "fname", label: "First Name" },
  3. { key: "lname", label: "Last Name" },
  4. { key: "age", label: "Age" }
  5. ];
  6.  
var myColumnDefs = [
    { key: "fname", label: "First Name" },
    { key: "lname", label: "Last Name" },
    { key: "age", label: "Age" }
];
 

The following Column properties are supported:

Column properties
PropertyTypeDescription
keyStringThe unique name assigned to each Column. When a Column key maps to a DataSource field, cells of the Column will automatically populate with the corresponding data. If a key is not defined in the Column definition, one will be auto-generated. Please see the Column Keys section for more uses.
fieldStringThe DataSource field mapped to the Column. By default, the field value is assigned to be the Column's key. Implementers may specify a different field explicitly in the Column definition. This feature is useful when mapping multiple Columns to a shared field, since keys must remain unique, or when the field name contains characters invalid for DOM or CSS usage (see the Column Keys section for more information).
labelStringBy default, the <th> element is populated with the Column's key. Supply a label to display a different header.
abbrStringValue for the <th> element's abbr attribute.
childrenObject[]An array of object literals that define nested child Columns of a Column.
classNameStringA custom CSS className or array of classNames to be applied to every cell of the Column.
editorStringString pointer to a CellEditor class.
editorOptionsObjectObject literal CellEditor-specific configuration options. Please refer to the API documentation for more information on which properties are supported for each type of CellEditor.
formatterString | HTMLFunctionA function or a pointer to a function to handle HTML formatting of cell data.
hiddenBooleanTrue if Column is hidden.
maxAutoWidthNumberUpper limit pixel width that a Column should auto-size to when its width is not set. Please note that maxAutoWidth validation is executed after cells are rendered, which may cause a visual flicker of content, especially on non-scrolling DataTables.
minWidthNumberMinimum pixel width. Please note that minWidth validation is executed after cells are rendered, which may cause a visual flicker of content, especially on non-scrolling DataTables.
resizeableBooleanTrue if Column is resizeable. The Drag & Drop Utility is required to enable this feature. Only bottom-level and non-nested Columns are resizeble.
selectedBooleanTrue if Column is selected.
sortableBooleanTrue if Column is sortable.
sortOptionsObjectObject literal of configurations for sort behavior.
  • defaultDir "asc", "desc", YAHOO.widget.DataTable.CLASS_ASC, or YAHOO.widget.CLASS_DESC
  • sortFunction Custom sort function.
widthNumberPixel width.

Basic Features

The DataTable class and its related classes provides many configuration parameters for you to fine-tune the user experience of your DataTable instance.

Column Keys Usage

Unique Column keys are assets that tie together data consumption and UI rendering in DataTable. When a Column key maps to a DataSource field, cells of the Column will automatically populate with the corresponding data. When a key does not map to a DataSource field, the cell will be left blank or can be populated manually, through the use of a formatter.

Column keys are also assigned as classnames to allow CSS hooks for customizing the UI. Defining custom CSS for ".yui-dt-col-myKey" and/or ".yui-dt-col-myKey .yui-dt-liner" lets you easily define styles per Column.

Internal usage of Column keys in the document include element IDs, names, classnames, headers, and href attributes, as well as selectors in dynamically created CSS rules. To support this kind of usage, Column keys must first be sanitized, to strip the string of characters that may be problematic for CSS and DOM usage (i.e., make sure it contains only letters, numbers, hyphen, or underscore). If your DataSource field contains characters that are invalid for DOM and/or CSS usage, it is recommended that you define the Column key as a simple alphanumeric string and also define a Column field that points to the problematic DataSource field.

Progressive Enhancement of Markup

By progressively enhancing <table> markup that is already on the page, you can make core content available to end users who do not have JavaScript enabled, while delivering a fully functional DataTable control to users who do have JavaScript enabled -- all from the same code base.

To this end, the DataSource accepts an HTML <table> element as a source of data. The DataTable will parse the data out of the table, remove it from the DOM, and replace it with an enhanced control.

  1. <div id="myMarkedUpContainer">
  2. <table id="myTable">
  3. <thead>
  4. <tr>
  5. <th>AAA</th>
  6. <th>BBB</th>
  7. <th>CCC</th>
  8. </tr>
  9. </thead>
  10. <tbody>
  11. <tr>
  12. <td>1</td>
  13. <td>bbb</td>
  14. <td>ccc</td>
  15. </tr>
  16. <tr>
  17. <td>2</td>
  18. <td>bbb</td>
  19. <td>ccc</td>
  20. </tr>
  21. <tr>
  22. <td>3</td>
  23. <td>bbb</td>
  24. <td>ccc</td>
  25. </tr>
  26. </tbody>
  27. </table>
  28. </div>
  29.  
<div id="myMarkedUpContainer">
    <table id="myTable">
        <thead>
            <tr>
                <th>AAA</th>
                <th>BBB</th>
                <th>CCC</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>bbb</td>
                <td>ccc</td>
            </tr>
            <tr>
                <td>2</td>
                <td>bbb</td>
                <td>ccc</td>
            </tr>
            <tr>
                <td>3</td>
                <td>bbb</td>
                <td>ccc</td>
            </tr>
        </tbody>
    </table>
</div>
 
  1. var myDataSource = new YAHOO.util.DataSource(YAHOO.util.Dom.get("myTable"));
  2. myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
  3. myDataSource.responseSchema = {
  4. fields: [
  5. { key: "AAA", parser:"number" },
  6. { key: "BBB" },
  7. { key: "CCC" }
  8. ]
  9. };
  10.  
  11. var myColumnDefs = [
  12. { key: "AAA" },
  13. { key: "BBB" },
  14. { key: "CCC" }
  15. ];
  16.  
  17. var myDataTable = new YAHOO.widget.DataTable("myMarkedUpContainer", myColumnDefs, myDataSource);
  18.  
var myDataSource = new YAHOO.util.DataSource(YAHOO.util.Dom.get("myTable"));
myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
myDataSource.responseSchema = {
    fields: [
        { key: "AAA", parser:"number" },
        { key: "BBB" },
        { key: "CCC" }
    ]
};
 
var myColumnDefs = [
    { key: "AAA" },
    { key: "BBB" },
    { key: "CCC" }
];
 
var myDataTable = new YAHOO.widget.DataTable("myMarkedUpContainer", myColumnDefs, myDataSource);
 

Implementers should note that form elements and elements attached to DOM event listeners may not be parsed correctly. Custom parsers should be used to extract data values out of form elements, and formatters should be used to reconstruct form elements programmatically. Likewise, DOM event listeners will be unattached from the original elements and implementers should listen for the corresponding DataTable Custom Events to rewire any lost functionality.

By default, DataSources with responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE, iterate over all TR elements of all TBODY elements of the given TABLE to parse out data. If there are any non-data elements (like summary rows or the message TBODY in DataTable), they should first be removed from the DOM.

Accessibility Features

Out of the box, the DataTable is accessible via screen readers and keyboard-navigable. Please take advantage of the following additional features to further enhance accessibility:

  1. /* Supply abbr values for THs */
  2. var myColumnDefs = [
  3. { label: "Amt Due", abbr: "Amount Due" },
  4. { label: "Rec'd", abbr: "Received" },
  5. { label: "YTD", abbr: "Year to Date" }
  6. ];
  7.  
  8. /* Supply caption and/or summary values for the TABLE */
  9. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  10. caption: "My Caption",
  11. summary: "My Summary"
  12. });
  13.  
/* Supply abbr values for THs */
var myColumnDefs = [
    { label: "Amt Due", abbr: "Amount Due" },
    { label: "Rec'd", abbr: "Received" },
    { label: "YTD", abbr: "Year to Date" }
];
 
/* Supply caption and/or summary values for the TABLE */
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
            caption: "My Caption",
            summary: "My Summary"
});
 

Custom Cell Formatting

The DataTable class provides a set of built-in static functions to format certain well-known types of data. In your Column definition, if you set a Column's formatter to YAHOO.widget.DataTable.formatDate, that function will render data of type Date with the default syntax of "MM/DD/YYYY". If you would like to bypass a built-in formatter in favor of your own, you can point a Column's formatter to a custom function that you define.

If you don't define any formatter, DataTable's default formatter will take your cell's data value as markup and insert it into the DOM with innerHTML. If you are using data from a third-party source, user input data, or any other untrustworthy source, it is highly recommended you point to the "text" formatter, which will HTML-escape data values for safer insertion into the DOM. See the Security Considerations section for more information.

As of the 2.3.0 release, the static formatter functions are called within the scope of the DataTable instance. Also as a convenience, the following string shortcuts may be used to point to the built-in formatter functions:

  • "button" points to YAHOO.widget.DataTable.formatButton
  • "checkbox" points to YAHOO.widget.DataTable.formatCheckbox
  • "currency" points to YAHOO.widget.DataTable.formatCurrency
  • "date" points to YAHOO.widget.DataTable.formatDate
  • "dropdown" points to YAHOO.widget.DataTable.formatDropdown
  • "email" points to YAHOO.widget.DataTable.formatEmail
  • "link" points to YAHOO.widget.DataTable.formatLink
  • "number" points to YAHOO.widget.DataTable.formatNumber
  • "radio" points to YAHOO.widget.DataTable.formatRadio
  • "text" points to YAHOO.widget.DataTable.formatText
  • "textarea" points to YAHOO.widget.DataTable.formatTextarea
  • "textbox" points to YAHOO.widget.DataTable.formatTextbox
  1. // Define a custom format function
  2. var myFormatDate = function (elCell, oRecord, oColumn, oData) {
  3. var oDate = oData, sMonth;
  4. switch(oDate.getMonth()) {
  5. case 0:
  6. sMonth = "Jan";
  7. break;
  8. case 1:
  9. sMonth = "Feb";
  10. break;
  11. case 2:
  12. sMonth = "Mar";
  13. break;
  14. case 3:
  15. sMonth = "Apr";
  16. break;
  17. case 4:
  18. sMonth = "May";
  19. break;
  20. case 5:
  21. sMonth = "Jun";
  22. break;
  23. case 6:
  24. sMonth = "Jul";
  25. break;
  26. case 7:
  27. sMonth = "Aug";
  28. break;
  29. case 8:
  30. sMonth = "Sep";
  31. break;
  32. case 9:
  33. sMonth = "Oct";
  34. break;
  35. case 10:
  36. sMonth = "Nov";
  37. break;
  38. case 11:
  39. sMonth = "Dec";
  40. break;
  41. }
  42. elCell.innerHTML = sMonth + " " + oDate.getDate() + ", " + oDate.getFullYear();
  43. };
  44.  
  45. var myColumnDefs = [
  46. { key: "Date", formatter: myFormatDate }, // Uses custom function myFormatDate
  47. { key: "Contact", formatter: "email" } // Uses built-in function YAHOO.widget.DataTable.formatEmail
  48. ];
  49.  
// Define a custom format function
var myFormatDate = function (elCell, oRecord, oColumn, oData) {
    var oDate = oData, sMonth;
    switch(oDate.getMonth()) {
        case 0:
            sMonth = "Jan";
            break;
        case 1:
            sMonth = "Feb";
            break;
        case 2:
            sMonth = "Mar";
            break;
        case 3:
            sMonth = "Apr";
            break;
        case 4:
            sMonth = "May";
            break;
        case 5:
            sMonth = "Jun";
            break;
        case 6:
            sMonth = "Jul";
            break;
        case 7:
            sMonth = "Aug";
            break;
        case 8:
            sMonth = "Sep";
            break;
        case 9:
            sMonth = "Oct";
            break;
        case 10:
            sMonth = "Nov";
            break;
        case 11:
            sMonth = "Dec";
            break;
    }
    elCell.innerHTML = sMonth + " " + oDate.getDate()  + ", " + oDate.getFullYear();
};
 
var myColumnDefs = [
    { key: "Date", formatter: myFormatDate }, // Uses custom function myFormatDate
    { key: "Contact", formatter: "email" } // Uses built-in function YAHOO.widget.DataTable.formatEmail
];
 

Nested Column Headers

If you'd like your DataTable to display nested headers, the control will automatically correlate the correct set of headers to each <td> element in order to support screen reader accessibility. In your Column definitions, any given Column object literal can itself host an array of Column object literals. Use the children property to assign descendant Columns. The following Column properties will cascade to descendant Columns if they are defined by a parent but not defined explicitly by children.

  • className
  • editor
  • editorOptions
  • formatter
  • resizeable
  • sortable
  • width
  1. var myColumnDefs = [
  2. { key: "page", label:"Page URL" },
  3. { key: "Statistics", className: "myClass", children: [ // className will cascade to children
  4. { key:"Visits",
  5. children: [
  6. { key: "visitsmonth", label: "This Month" },
  7. { key: "visitsytd", label: "YTD", abbr: "Year to Date" }
  8. ]
  9. },
  10. { key: "Views",
  11. children: [
  12. { key: "viewsmonth", label: "This Month" },
  13. { key: "viewsytd", label: "YTD", abbr: "Year to Date" }
  14. ]
  15. }
  16. ]}
  17. ];
  18.  
var myColumnDefs = [
    { key: "page", label:"Page URL" },
    { key: "Statistics", className: "myClass", children: [ // className will cascade to children
        { key:"Visits",
            children: [
                { key: "visitsmonth", label: "This Month" },
                { key: "visitsytd", label: "YTD", abbr: "Year to Date" }
            ]
        },
        { key: "Views",
            children: [
                { key: "viewsmonth", label: "This Month" },
                { key: "viewsytd", label: "YTD", abbr: "Year to Date" }
            ]
        }
    ]}
];
 

Please note that not all features are compatible with nested Columns. Specifically, Columns that have children can not be resized, and Columns that are children can neither be hidden/shown nor dragged-and-dropped.

Basic Column Sorting

By default, if a Column is defined with sortable:true, then clicking on the Column header will execute a basic sort function that supports comparisons of Strings, Numbers, and Dates.

  1. var myColumnDefs = [
  2. { key: "name", label: "Dog's Name", sortable: true },
  3. { key: "age", label: "Dog's Age", formatter: "number", sortable: true },
  4. { key: "bday", label: "Dog's Birthday", formatter: "date", sortable: true }
  5. ];
  6.  
var myColumnDefs = [
    { key: "name", label: "Dog's Name", sortable: true },
    { key: "age", label: "Dog's Age", formatter: "number", sortable: true },
    { key: "bday", label: "Dog's Birthday", formatter: "date", sortable: true }
];
 

Keep in mind that sorting a Column first sorts the data in the underlying RecordSet and then updates the DOM UI to reflect this new sort order. Therefore, the type of the data (e.g., String, Number, Date, etc.) held in the RecordSet determines the sort algorithm, not the type as defined in your Column definition formatter property.

In general, the RecordSet expects to hold data in native JavaScript types. For instance, a date is expected to be a JavaScript Date instance, not a string like "4/26/2005" in order to sort properly. Converting data types as data comes into your RecordSet is enabled through the parser property in the fields array of your DataSource's responseSchema. This is especially useful when data is coming over XHR as a String and needs to be converted to a Number, Boolean, Date, etc. A custom function can be defined, or you can use one of the static built-in functions:

  1. myDataSource.responseSchema = {
  2. fields: [
  3. { key: "name", parser: "string" }, // Stores incoming data as Strings
  4. { key: "age", parser: "number" }, // Stores incoming data as Numbers
  5. { key: "bday", parser: "date" }, // Stores incoming data as Dates
  6. { key: "custom", parser: myParser } // Applies a custom function to incoming data
  7. ]
  8. };
  9.  
myDataSource.responseSchema = {
    fields: [
        { key: "name", parser: "string" },  // Stores incoming data as Strings
        { key: "age", parser: "number" },   // Stores incoming data as Numbers
        { key: "bday", parser: "date" },    // Stores incoming data as Dates
        { key: "custom", parser: myParser } // Applies a custom function to incoming data
    ]
};
 

If your initial data is already sorted, be sure to specify the DataTable config property sortedBy to display the proper UI at instantiation. The value of the property should be either the class constant YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC. Now when the user goes to sort this Column, DataTable will know to sort it in the correct (opposite) direction. Note that setting the sortedBy property does not perform a sort on the Column, it merely applies the appropriate CSS when the DataTable loads.

  1. // The first click on the "receivedDate" Column will sort in descending order
  2. var myDataTable = new YAHOO.widget.DataTable("sorted", myColumnDefs, myDataSource, {
  3. sortedBy : {
  4. key: "receivedDate",
  5. dir: YAHOO.widget.DataTable.CLASS_ASC
  6. }
  7. });
  8.  
// The first click on the "receivedDate" Column will sort in descending order
var myDataTable = new YAHOO.widget.DataTable("sorted", myColumnDefs, myDataSource, {
    sortedBy : {
        key: "receivedDate",
        dir: YAHOO.widget.DataTable.CLASS_ASC
    }
});
 

Advanced Column Sorting

The sortOptions property can be used in your Column definitions to specify more advanced sort options. If a Column's default sort direction should be in descending order rather than ascending order, as is often the case for reverse-chronological date fields, be sure to define the defaultDir property on sortOptions:

  1. var myColumnDefs = [
  2. { key: "name", label: "Dog's Name", sortable: true },
  3. { key: "age", label: "Dog's Age", formatter: "number", sortable: true },
  4. { key: "bday", label: "Dog's Birthday", formatter: "date", sortable: true, sortOptions: { defaultDir: YAHOO.widget.DataTable.CLASS_DESC } } // First click sorts latest on top
  5. ];
  6.  
var myColumnDefs = [
    { key: "name", label: "Dog's Name", sortable: true },
    { key: "age", label: "Dog's Age", formatter: "number", sortable: true },
    { key: "bday", label: "Dog's Birthday", formatter: "date", sortable: true, sortOptions: { defaultDir: YAHOO.widget.DataTable.CLASS_DESC } } // First click sorts latest on top
];
 

Specifying a sortOptions.field value will point the sort to be executed on the given field.

  1. var myColumnDefs = [
  2. { key: "name", label: "Dog's Name", sortable: true },
  3. { key: "age", label: "Dog's Age", formatter: "number", sortable: true },
  4. { key: "bday_string", label: "Dog's Birthday", sortable: true, sortOptions: { field: "bday_date" } } // Sort occurs on a non-rendered data field
  5. ];
  6.  
var myColumnDefs = [
    { key: "name", label: "Dog's Name", sortable: true },
    { key: "age", label: "Dog's Age", formatter: "number", sortable: true },
    { key: "bday_string", label: "Dog's Birthday", sortable: true, sortOptions: { field: "bday_date" } } // Sort occurs on a non-rendered data field
];
 

More complex data structures may require custom sort algorithms. You can provide custom sort functions in your Column definition via the sortOptions property. The code below is an example of how to implement nested sorting, where clicking on Column2's header will sort by values in Column2 and, if there are equal values, further sort by values in Column1.

Please note that as of the 2.3.0 release, a single sort function is used for both ascending and descending sorts.

  1. // Custom function that defaults to Column1 value if values are equal
  2. var mySortFunction = function(a, b, desc, field) {
  3. // Deal with empty values
  4. if(!YAHOO.lang.isValue(a)) {
  5. return (!YAHOO.lang.isValue(b)) ? 0 : 1;
  6. } else if(!YAHOO.lang.isValue(b)) {
  7. return -1;
  8. }
  9.  
  10. // First compare by Column2
  11. var comp = YAHOO.util.Sort.compare;
  12. var compState = comp(a.getData(field), b.getData(field), desc);
  13.  
  14. // If values are equal, then compare by Column1
  15. return (compState !== 0) ? compState : comp(a.getData("Column1"), b.getData("Column1"), desc);
  16. };
  17.  
  18. var myColumnDefs = [
  19. { key: "Column1", sortable: true }, // Uses default sort function
  20. { key: "Column2", sortable: true, // Uses custom sort function
  21. // Provide sortOptions as an object literal
  22. sortOptions: { sortFunction: mySortFunction }
  23. }
  24. ];
  25.  
// Custom function that defaults to Column1 value if values are equal
var mySortFunction = function(a, b, desc, field) {
    // Deal with empty values
    if(!YAHOO.lang.isValue(a)) {
        return (!YAHOO.lang.isValue(b)) ? 0 : 1;
    } else if(!YAHOO.lang.isValue(b)) {
        return -1;
    }
 
    // First compare by Column2
    var comp = YAHOO.util.Sort.compare;
    var compState = comp(a.getData(field), b.getData(field), desc);
 
    // If values are equal, then compare by Column1
    return (compState !== 0) ? compState : comp(a.getData("Column1"), b.getData("Column1"), desc);
};
 
var myColumnDefs = [
    { key: "Column1", sortable: true }, // Uses default sort function
    { key: "Column2", sortable: true, // Uses custom sort function
            // Provide sortOptions as an object literal
            sortOptions: { sortFunction: mySortFunction }
    }
];
 

Pagination

Pagination can be used to reduce the real-estate footprint of DataTables with large data sets. As of the 2.5.0 release, implementors should use the new Paginator class to define pagination layout and behavior for the DataTable.

  1. var myConfigs = {
  2. paginator : new YAHOO.widget.Paginator({
  3. rowsPerPage: 50
  4. })
  5. };
  6.  
  7. var myDataTable = new YAHOO.widget.DataTable("myContainer",
  8. myColumnDefs, myDataSource, myConfigs);
  9.  
var myConfigs = {
    paginator : new YAHOO.widget.Paginator({
        rowsPerPage: 50
    })
};
 
var myDataTable = new YAHOO.widget.DataTable("myContainer",
    myColumnDefs, myDataSource, myConfigs);
 

By default, the DataTable will render a set of pagination controls above and below the table:

rendered pagination controls

Templates and UI components

The Paginator class uses a template and component architecture to afford the greatest flexibility in choosing how to render the pagination controls.

  1. var myConfigs = {
  2. paginator : new YAHOO.widget.Paginator({
  3. rowsPerPage: 50, // REQUIRED
  4. totalRecords: myData.length, // OPTIONAL
  5.  
  6. // use an existing container element
  7. containers: 'my_pagination',
  8.  
  9. // use a custom layout for pagination controls
  10. template: "{PageLinks} Show {RowsPerPageDropdown} per page",
  11.  
  12. // show all links
  13. pageLinks: YAHOO.widget.Paginator.VALUE_UNLIMITED,
  14.  
  15. // use these in the rows-per-page dropdown
  16. rowsPerPageOptions: [25, 50, 100],
  17.  
  18. // use custom page link labels
  19. pageLabelBuilder: function (page,paginator) {
  20. var recs = paginator.getPageRecords(page);
  21. return (recs[0] + 1) + ' - ' + (recs[1] + 1);
  22. }
  23. })
  24. };
  25.  
  26. var myDataTable = new YAHOO.widget.DataTable("myContainer",
  27. myColumnDefs, myDataSource, myConfigs);
  28.  
var myConfigs = {
    paginator : new YAHOO.widget.Paginator({
        rowsPerPage: 50, // REQUIRED
        totalRecords: myData.length, // OPTIONAL
 
        // use an existing container element
        containers: 'my_pagination',
 
        // use a custom layout for pagination controls
        template: "{PageLinks} Show {RowsPerPageDropdown} per page",
 
        // show all links
        pageLinks: YAHOO.widget.Paginator.VALUE_UNLIMITED,
 
        // use these in the rows-per-page dropdown
        rowsPerPageOptions: [25, 50, 100],
 
        // use custom page link labels
        pageLabelBuilder: function (page,paginator) {
            var recs = paginator.getPageRecords(page);
            return (recs[0] + 1) + ' - ' + (recs[1] + 1);
        }
    })
};
 
var myDataTable = new YAHOO.widget.DataTable("myContainer",
    myColumnDefs, myDataSource, myConfigs);
 

This Paginator configuration would result in the following UI:

rendered pagination controls with a custom template

Paginator instances use the configuration attribute template to describe the markup for rendering the pagination controls. The template string contains placeholders to identify where the various control elements should be located. Each placeholder in the template is the class name of a UI component found in the YAHOO.widget.Paginator.ui namespace.

The default template is "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}".

The following UI components are provided in the 2.5.1 release:

  • FirstPageLink
  • PreviousPageLink
  • NextPageLink
  • LastPageLink
  • PageLinks
  • RowsPerPageDropdown
  • CurrentPageReport

For custom pagination controls, create new UI component classes in the YAHOO.widget.Paginator.ui namespace and reference them in your Paginator template.

See the API docs for Paginator and each UI component class for available configuration.

Scrolling

Scrolling is another way to conserve the UI footprint of larger data sets. In the DataTable control, scrolling can be

  • x-scrolling: horizontal scrolling where Column headers track with body
  • y-scrolling: vertical scrolling where Column headers are fixed while body scrolls
  • xy-scrolling: combined horizontal and vertical scrolling where Column headers are fixed vertically but track horizontally

Note: As of the 2.6.0 release, passing scrollable:true to the DataTable constructor will actually instantiate the subclass ScrollingDataTable. This change means that scrolling cannot be enabled or disabled dynamically at runtime, and implementers who want the scrolling feature must specify it at instantiation, by calling the ScrollingDataTable constructor directly, or alternately either by passing in the config scrollable:true to the DataTable constructor. APIs that are relevant and necessary only for the scrolling feature have been moved to the ScrollingDataTable subclass and are no longer available from the DataTable class.

Implementers should keep in mind that the scrolling feature inherently entails a performance overhead that may impact user experience. Defining pixel widths for as many Columns as possible, disabling drag-and-drop resizing, and keeping your data size manageable will go a long way towards reducing that overhead.

To enable scrolling, call the YAHOO.widget.ScrollingDataTable constructor and define width and/or height String values. X-scrolling is enabled by only setting a width, y-scrolling is enabled by only setting a height, and xy-scrolling is enabled by setting both. When a height is set, it defines the height of the vertically scrolling body of the table, not including headers.

  1. /* enables x-scrolling */
  2. var myDataTableX = new YAHOO.widget.ScrollingDataTable("myContainer", myColumnDefs, myDataSource, {
  3. width: "40em" // width as a string value
  4. });
  5.  
  6. /* enables y-scrolling */
  7. var myDataTableY = new YAHOO.widget.ScrollingDataTable("myContainer", myColumnDefs, myDataSource, {
  8. height: "400px" // height as a string value
  9. });
  10.  
  11. /* enables xy-scrolling */
  12. var myDataTableXY = new YAHOO.widget.ScrollingDataTable("myContainer", myColumnDefs, myDataSource, {
  13. width: "40em" // width as a string value
  14. height: "400px" // height as a string value
  15. });
  16.  
/* enables x-scrolling */
var myDataTableX = new YAHOO.widget.ScrollingDataTable("myContainer", myColumnDefs, myDataSource, {
    width: "40em" // width as a string value
});
 
/* enables y-scrolling */
var myDataTableY = new YAHOO.widget.ScrollingDataTable("myContainer", myColumnDefs, myDataSource, {
    height: "400px" // height as a string value
});
 
/* enables xy-scrolling */
var myDataTableXY = new YAHOO.widget.ScrollingDataTable("myContainer", myColumnDefs, myDataSource, {
    width: "40em" // width as a string value
    height: "400px" // height as a string value
});
 

Adjusting the Render Loop Size

In cases where your DataTable needs to display the entirety of a very large set of data, the renderLoopSize config can help manage browser DOM rendering so that the UI thread does not get locked up on very large tables. Any value greater than 0 will cause the DOM rendering to be executed in setTimeout() chains that render the specified number of rows in each loop. The ideal value should be determined per implementation since there are no hard and fast rules, only general guidelines:

  • By default renderLoopSize is 0, so all rows are rendered in a single loop. A renderLoopSize > 0 adds overhead so use thoughtfully.
  • If your set of data is large enough (number of rows X number of Columns X formatting complexity) that users experience latency in the visual rendering and/or it causes the script to hang, consider setting a renderLoopSize.
  • A renderLoopSize under 50 probably isn't worth it. A renderLoopSize > 100 is probably better.
  • A data set is probably not considered large enough unless it has hundreds and hundreds of rows.
  • Having a renderLoopSize > 0 and < total rows does cause the table to be rendered in one loop (same as renderLoopSize = 0) but it also triggers functionality such as post-render row striping to be handled from a separate setTimeout thread.
  1. // Render 100 rows per loop
  2. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  3. renderLoopSize: 100
  4. });
  5.  
// Render 100 rows per loop
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
    renderLoopSize: 100
});
 

Row and Cell Selection Modes

You can enable row selection or cell selection by assigning the built-in onEventSelectRow event handler to a Custom Event.

  1. // Enables standard row selection
  2. myDataTable.subscribe("rowClickEvent", myDataTable.onEventSelectRow);
  3.  
// Enables standard row selection
myDataTable.subscribe("rowClickEvent", myDataTable.onEventSelectRow);
 

The property selectionMode is used to enable or disable modifier keys and define the multi-selection paradigm. By default, selectionMode is set to standard which enables the modifier keys <shift> and <ctrl> to select multiple rows.

Setting selectionMode to single enables row selection but disables the use of modifier keys for multi-selection.

  1. // Enables single-mode row selection
  2. myDataTable.set("selectionMode", "single");
  3. myDataTable.subscribe("rowClickEvent", myDataTable.onEventSelectRow);
  4.  
// Enables single-mode row selection
myDataTable.set("selectionMode", "single");
myDataTable.subscribe("rowClickEvent", myDataTable.onEventSelectRow);
 

Setting selectionMode to cellblock, cellrange, or singlecell enables cell selection in block mode, range mode, or single mode respectively.

  1. // Enables cell-block selection
  2. myDataTable.set("selectionMode", "cellblock");
  3. myDataTable.subscribe("cellClickEvent", myDataTable.onEventSelectCell);
  4.  
  5. // Enables cell-range selection
  6. myDataTable.set("selectionMode", "cellrange");
  7. myDataTable.subscribe("cellClickEvent", myDataTable.onEventSelectCell);
  8.  
  9. // Enables cell-block selection (modifier keys are disabled)
  10. myDataTable.set("selectionMode", "singlecell");
  11. myDataTable.subscribe("cellClickEvent", myDataTable.onEventSelectCell);
  12.  
// Enables cell-block selection
myDataTable.set("selectionMode", "cellblock");
myDataTable.subscribe("cellClickEvent", myDataTable.onEventSelectCell);
 
// Enables cell-range selection
myDataTable.set("selectionMode", "cellrange");
myDataTable.subscribe("cellClickEvent", myDataTable.onEventSelectCell);
 
// Enables cell-block selection (modifier keys are disabled)
myDataTable.set("selectionMode", "singlecell");
myDataTable.subscribe("cellClickEvent", myDataTable.onEventSelectCell);
 

Take care that the selectionMode value matches the mode you have enabled. If you have assigned the onEventSelectCell handler, then selectionMode needs to be set to "cellblock", "cellrange", or "singlecell" exclusively. Conversely, the values "standard" and "single" are reserved for DataTables with row selection enabled. Finally, mixing and matching row and cell selection paradigms is not recommended.

The methods selectRow() and selectCell() also allow you trigger selection programmatically:

  1. // Selects the first row on the page
  2. myDataTable.selectRow(myDataTable.getTrEl(0));
  3.  
  4. // Selects any cell that receives a checkbox click
  5. myDataTable.subscribe("checkboxClickEvent", function(oArgs) {
  6. var elCheckbox = oArgs.target;
  7. if(elCheckbox.checked) {
  8. var elCell = this.getTdEl(elCheckbox);
  9. this.selectCell(elCell);
  10. }
  11. });
  12.  
// Selects the first row on the page
myDataTable.selectRow(myDataTable.getTrEl(0));
 
// Selects any cell that receives a checkbox click
myDataTable.subscribe("checkboxClickEvent", function(oArgs) {
    var elCheckbox = oArgs.target;
    if(elCheckbox.checked) {
        var elCell = this.getTdEl(elCheckbox);
        this.selectCell(elCell);
    }
});
 

The following methods allow you to retrieve selected rows and cells as an array:

  1. // Returns an array of selected Record IDs (across all pages, if pagination is enabled)
  2. myDataTable.getSelectedRows();
  3.  
  4. // Returns an array of selected TR element references (on current page only, if pagination is enabled)
  5. myDataTable.getSelectedTrEls();
  6.  
// Returns an array of selected Record IDs (across all pages, if pagination is enabled)
myDataTable.getSelectedRows();
 
// Returns an array of selected TR element references (on current page only, if pagination is enabled)
myDataTable.getSelectedTrEls();
 

The look and feel of a selected element can be customized via CSS:

  1. /* white on black */
  2. #myDataTable .yui-dt-selected {
  3. background-color: #000;
  4. color: #FFF;
  5. }
/* white on black */
#myDataTable .yui-dt-selected {
    background-color: #000;
    color: #FFF;
}

Column Selection

You can enable Column selection by assigning the built-in onEventSelectColumn event handler to a Custom Event.

  1. // Enables Column selection
  2. myDataTable.subscribe("theadCellDblclickEvent", myDataTable.onEventSelectColumn);
  3.  
// Enables Column selection
myDataTable.subscribe("theadCellDblclickEvent", myDataTable.onEventSelectColumn);
 

The method selectColumn() also allows you trigger selection programmatically:

  1. // Selects the first Column
  2. myDataTable.selectColumn(0);
  3.  
  4. // Selects Column when cell is clicked
  5. myDataTable.subscribe("cellClickEvent", function (oArgs) {
  6. this.selectColumn(this.getColumn(oArgs.target));
  7. });
  8.  
// Selects the first Column
myDataTable.selectColumn(0);
 
// Selects Column when cell is clicked
myDataTable.subscribe("cellClickEvent", function (oArgs) {
    this.selectColumn(this.getColumn(oArgs.target));
});
 

The following methods allow you to retrieve selected Columns as an array:

  1. // Returns an array of selected Column instances
  2. myDataTable.getSelectedColumns();
  3.  
// Returns an array of selected Column instances
myDataTable.getSelectedColumns();
 

The look and feel of a selected element can be customized via CSS:

  1. /* white on black */
  2. #myDataTable .yui-dt-selected {
  3. background-color: #000;
  4. color: #FFF;
  5. }
  6.  
/* white on black */
#myDataTable .yui-dt-selected {
    background-color: #000;
    color: #FFF;
}
 

Cell, Row, and Column Highlighting

You can enable cell, row or Column highlighting by assigning the appropriate built-in event handlers to Custom Events. Please note that mixing and matching cell, row and Column highlighting is not recommended.

  1. // Enables cell highlighting
  2. myCellHighlightDataTable.subscribe("cellMouseoverEvent", myCellHighlightDataTable.onEventHighlightCell);
  3. myCellHighlightDataTable.subscribe("cellMouseoutEvent", myCellHighlightDataTable.onEventUnhighlightCell);
  4.  
  5. // Enables row highlighting
  6. myRowHighlightDataTable.subscribe("rowMouseoverEvent", myRowHighlightDataTable.onEventHighlightRow);
  7. myRowHighlightDataTable.subscribe("rowMouseoutEvent", myRowHighlightDataTable.onEventUnhighlightRow);
  8.  
  9. // Enables Column highligting
  10. myColHighlightDataTable.subscribe("theadCellMouseoverEvent", myColHighlightDataTable.onEventHighlightColumn);
  11. myColHighlightDataTable.subscribe("theadCellMouseoutEvent", myColHighlightDataTable.onEventUnhighlightColumn);
  12.  
// Enables cell highlighting
myCellHighlightDataTable.subscribe("cellMouseoverEvent", myCellHighlightDataTable.onEventHighlightCell);
myCellHighlightDataTable.subscribe("cellMouseoutEvent", myCellHighlightDataTable.onEventUnhighlightCell);
 
// Enables row highlighting
myRowHighlightDataTable.subscribe("rowMouseoverEvent", myRowHighlightDataTable.onEventHighlightRow);
myRowHighlightDataTable.subscribe("rowMouseoutEvent", myRowHighlightDataTable.onEventUnhighlightRow);
 
// Enables Column highligting
myColHighlightDataTable.subscribe("theadCellMouseoverEvent", myColHighlightDataTable.onEventHighlightColumn);
myColHighlightDataTable.subscribe("theadCellMouseoutEvent", myColHighlightDataTable.onEventUnhighlightColumn);
 

The look and feel of a highlighted element can be customized via CSS:

  1. /* white on black */
  2. #myDataTable .yui-dt-highlighted {
  3. background-color: #000;
  4. color: #FFF;
  5. }
  6.  
/* white on black */
#myDataTable .yui-dt-highlighted {
    background-color: #000;
    color: #FFF;
}
 

More Column APIs

Columns may be hidden, shown, inserted, or deleted. At this time, these APIs are only supported for Columns which are not nested children of other Columns. The methods hideColumn() and showColumn() merely adjust the width values in the UI. However, removeColumn() and insertColumn() will remove and insert Column instances from the actual ColumnSet.

  1. // Get a Column
  2. var oColumn = myDataTable.getColumn(3);
  3.  
  4. // Hide Column
  5. myDataTable.hideColumn(oColumn);
  6.  
  7. // Show Column
  8. myDataTable.showColumn(oColumn);
  9.  
  10. // Remove Column
  11. var myRemovedColumn = myDataTable.removeColumn(oColumn);
  12.  
  13. // Insert the removed Column into position 0
  14. myDataTable.insertColumn(myRemovedColumn, 0);
  15.  
  16. // Insert a new Column into position 3
  17. myDataTable.insertColumn({
  18. key: "newCol",
  19. label: "New Column"
  20. }, 3);
  21.  
// Get a Column
var oColumn = myDataTable.getColumn(3);
 
// Hide Column
myDataTable.hideColumn(oColumn);
 
// Show Column
myDataTable.showColumn(oColumn);
 
// Remove Column
var myRemovedColumn = myDataTable.removeColumn(oColumn);
 
// Insert the removed Column into position 0
myDataTable.insertColumn(myRemovedColumn, 0);
 
// Insert a new Column into position 3
myDataTable.insertColumn({
    key: "newCol",
    label: "New Column"
}, 3);
 

If the Drag & Drop Utility is available on the page, Column reordering can be enabled in your DataTable constructor:

  1. // Enable Drag & Drop reordering
  2. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  3. draggableColumns: true
  4. });
  5.  
// Enable Drag & Drop reordering
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
    draggableColumns: true
});
 

Inline Cell Editing

Inline cell editing can be enabled by assigning the built-in onEventShowCellEditor event handler to a Custom Event. Assign a CellEditor instance to the editor property in your Column definition:

  1. // Define inline cell editing
  2. var myColumnDefs = [
  3. { key: "group", editor: new YAHOO.widget.DropdownCellEditor({ dropdownOptions: [ "one","two","three" ] } )},
  4. { key: "date", editor: new YAHOO.widget.DateCellEditor(), formatter: YAHOO.widget.DataTable.formatDate },
  5. { key: "amount", editor: new YAHOO.widget.TextboxCellEditor({ validator: YAHOO.widget.DataTable.validateNumber } ) },
  6. { key: "active", editor: new YAHOO.widget.RadioCellEditor({ radioOptions: [ "yes","no","maybe" ], disableBtns: true } ) },
  7. { key: "colors", editor: new YAHOO.widget.CheckboxCellEditor({ checkboxOptions: [ "red","yellow","blue" ] } ) }
  8. ];
  9.  
  10. myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, this.myDataSource);
  11.  
  12. // Enable inline cell editing
  13. myDataTable.subscribe("cellClickEvent", this.myDataTable.onEventShowCellEditor);
  14.  
// Define inline cell editing
var myColumnDefs = [
    { key: "group", editor: new YAHOO.widget.DropdownCellEditor({ dropdownOptions: [ "one","two","three" ] } )},
    { key: "date", editor: new YAHOO.widget.DateCellEditor(), formatter: YAHOO.widget.DataTable.formatDate },
    { key: "amount", editor: new YAHOO.widget.TextboxCellEditor({ validator: YAHOO.widget.DataTable.validateNumber } ) },
    { key: "active", editor: new YAHOO.widget.RadioCellEditor({ radioOptions: [ "yes","no","maybe" ], disableBtns: true } ) },
    { key: "colors", editor: new YAHOO.widget.CheckboxCellEditor({ checkboxOptions: [ "red","yellow","blue" ] } ) }
];
 
myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, this.myDataSource);
 
// Enable inline cell editing
myDataTable.subscribe("cellClickEvent", this.myDataTable.onEventShowCellEditor);
 

You can pass in configuration options into the CellEditor constructor as an object literal to customize certain behaviors of the editor, such as input validation or default values. Please refer to the API documentation for more information on which properties are supported for each type of CellEditor.

Common CellEditor properties
PropertyTypeDescription
asyncSubmitterFunctionImplementer defined function that can submit the input value to a server. This function must accept the arguments fnCallback and oNewValue. When the submission is complete, the function must also call fnCallback(bSuccess, oNewValue) to finish the save routine in the CellEditor. This function can also be used to perform extra validation or input value manipulation.
defaultValueMixedDefault value to use in CellEditor if Record data value is undefined.
disableBtnsBooleanTrue if Save/Cancel buttons should not be displayed in the CellEditor.
LABEL_CANCELStringText to display on Cancel button.
LABEL_SAVEStringText to display on Save button.
validatorFunctionValidator function for input data returns either the validated (or type-converted) value or undefined. Implementers can use the built-in validator function YAHOO.widget.DataTable.validateNumber or provide their own custom function.

Data Retrieval

Customizing the DataSource Request Syntax

DataTable provides a built in generateRequest Attribute, which constructs a string-based request with a default syntax:

"sort={SortColumnKey}&dir={SortColumnDir}&startIndex={PaginationStartIndex}&results={PaginationRowsPerPage}"

The default syntax can be customized by setting a custom function for the generateRequest Attribute:

  1. var myRequestBuilder = function(oState, oSelf) {
  2. // Get states or use defaults
  3. oState = oState || { pagination: null, sortedBy: null };
  4. var sort = (oState.sortedBy) ? oState.sortedBy.key : "myDefaultColumnKey";
  5. var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "false" : "true";
  6. var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
  7. var results = (oState.pagination) ? oState.pagination.rowsPerPage : 100;
  8.  
  9. // Build custom request
  10. return "column=" + sort +
  11. "&asc=" + true +
  12. "&pageStart=" + startIndex +
  13. "&pageEnd=" + (startIndex + results - 1);
  14. };
  15.  
  16. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  17. generateRequest: myRequestBuilder
  18. });
  19.  
var myRequestBuilder = function(oState, oSelf) {
    // Get states or use defaults
    oState = oState || { pagination: null, sortedBy: null };
    var sort = (oState.sortedBy) ? oState.sortedBy.key : "myDefaultColumnKey";
    var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "false" : "true";
    var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
    var results = (oState.pagination) ? oState.pagination.rowsPerPage : 100;
 
    // Build custom request
    return  "column=" + sort +
            "&asc=" + true +
            "&pageStart=" + startIndex +
            "&pageEnd=" + (startIndex + results - 1);
};
 
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
    generateRequest: myRequestBuilder
});
 

Loading Initial Data

By default, DataTable will fire off a request for data to the DataSource at instantiation. You can disable this feature by setting the config initialLoad to false in the constructor:

  1. // Do not load data at instantiation
  2. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  3. initialLoad: false
  4. });
  5.  
// Do not load data at instantiation
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
    initialLoad: false
});
 

If, however, you would like to specify the request sent to the DataSource in the initial load, you can set that value via the initialRequest config:

  1. // Load 10 orders at instantiation
  2. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  3. initialRequest: "query=orders&results=10"
  4. });
  5.  
// Load 10 orders at instantiation
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
    initialRequest: "query=orders&results=10"
});
 

You can further customize the initial request that is sent to the DataSource by setting the initialLoad config to be an object literal. Doing so allows you to pass in an arbitrary payload of data that will be accessible to you in the callback loop:

  1. // Load 10 orders at instantiation and pass back the tracking ID
  2. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
  3. initialLoad: {
  4. request: "query=orders&results=10",
  5. argument: "12345"
  6. }
  7. });
  8.  
  9. // Now oPayload is available in the callback loop
  10. myDataTable.doBeforeLoadData = function (sRequest, oResponse, oPayload) {
  11. // When response returns, oPayload is "12345"
  12. // and available in the doBeforeLoadData customizable method
  13. return true;
  14. };
  15.  
// Load 10 orders at instantiation and pass back the tracking ID
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, {
    initialLoad: {
        request: "query=orders&results=10",
        argument: "12345"
    }
});
 
// Now oPayload is available in the callback loop
myDataTable.doBeforeLoadData = function (sRequest, oResponse, oPayload) {
    // When response returns, oPayload is "12345"
    // and available in the doBeforeLoadData customizable method
    return true;
};
 

Loading Data at Runtime

The DataTable provides built-in functions that can be used as callbacks to DataSource's sendRequest method:

onDataReturnInitializeTable
Draws a DataTable with the data provided. On an existing DataTable it will delete the current contents and states before drawing the new. It is the method that would be used with polling to refresh the DataTable with new data.
onDataReturnInsertRows
This method will insert new rows at the beginning of the DataTable, preserving the existing data and regardless of any sort order. The insertion index for the added records can be assigned to when sending the request to the DataSource, or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime. If applicable, creates or updates corresponding TR elements.
onDataReturnAppendRows
Rows will be added to the end of the DataTable, preserving the existing records, regardless of sort order.
onDataReturnSetRows
This method will manage DataSource responses according to oPayload data to create Records at an index corresponding to the request (i.e., if data for page 3 was requested, the Records will be placed at an index corresponding to page 3).

Data can be loaded at runtime with a sendRequest() call to the DataSource, by passing in a DataTable method as a callback handler.

  1. // Sends a request to the DataSource for more data
  2. var oCallback = {
  3. success : myDataTable.onDataReturnAppendRows,
  4. failure : myDataTable.onDataReturnAppendRows,
  5. scope : myDataTable,
  6. argument: myDataTable.getState() // data payload that will be returned to the callback function
  7. };
  8. this.myDataSource.sendRequest("query=orders&results=10", oCallback);
  9.  
// Sends a request to the DataSource for more data
var oCallback = {
    success : myDataTable.onDataReturnAppendRows,
    failure : myDataTable.onDataReturnAppendRows,
    scope : myDataTable,
    argument: myDataTable.getState() // data payload that will be returned to the callback function
};
this.myDataSource.sendRequest("query=orders&results=10", oCallback);
 

As a convenience, DataTable has a load() method that will send the initialRequest value to the DataSource, and point to the onDataReturnInitializeTable() built-in callback method . The method accepts a config object that can specify request, callback, and datasource values.

  1. // Sends the "initialRequest" config value to the DataSource for more data
  2. // and calls the built-in "onDataReturnInitializeTable()" callback method.
  3. myDataTable.load();
  4.  
  5. // Override all the default values
  6. myDataTable.load({
  7. request: "anotherRequest",
  8. callback: {
  9. success: myDataTable.onDataReturnAppendRows,
  10. failure: myDataTable.onDataReturnAppendRows,
  11. scope: myDataTable,
  12. argument: myDataTable.getState()
  13. },
  14. datasource: anotherDataSource
  15. });
  16.  
// Sends the "initialRequest" config value to the DataSource for more data
// and calls the built-in "onDataReturnInitializeTable()" callback method.
myDataTable.load();
 
// Override all the default values
myDataTable.load({
    request: "anotherRequest",
    callback: {
        success: myDataTable.onDataReturnAppendRows,
        failure: myDataTable.onDataReturnAppendRows,
        scope: myDataTable,
        argument: myDataTable.getState()
    },
    datasource: anotherDataSource
});
 

Manipulating The Data Payload

The handleDataReturnPayload() method gives implementers the opportunity to access and manipulate the data payload returned to the callback function. The payload object usually represents DataTable's state values, in order to preserve state across data requests. You can override the function like this:

  1. myDataTable.handleDataReturnPayload = function (oRequest, oResponse, oPayload) {
  2. // The payload object usually represents DataTable's state values, including:
  3. // oPayload.totalRecords = [number of total records]
  4. // oPayload.pagination.rowsPerPage = [number of rows per page]
  5. // oPayload.pagination.recordOffset = [index of first record of current page]
  6. // oPayload.sortedBy.key = [key of currently sorted column]
  7. // oPayload.sortedBy.dir = [direction of currently sorted column]
  8.  
  9. return oPayload;
  10. };
  11.  
myDataTable.handleDataReturnPayload = function (oRequest, oResponse, oPayload) {
    // The payload object usually represents DataTable's state values, including:
    // oPayload.totalRecords = [number of total records]
    // oPayload.pagination.rowsPerPage = [number of rows per page]
    // oPayload.pagination.recordOffset = [index of first record of current page]
    // oPayload.sortedBy.key =  [key of currently sorted column]
    // oPayload.sortedBy.dir = [direction of currently sorted column]
 
    return oPayload;
};
 

Server-side Sorting and Pagination of Dynamic Data

When data is very dynamic in nature, or too large to store locally in JavaScript, sorting and pagination functionality is best implemented on the server side. When dynamicData is enabled, all pagination and sorting interactions trigger a DataSource request for the new set of data. State management is taken care of for you via an oState object literal which gets sent as a data payload of the sendRequest() method and then handled in callback function along with the response.

Please note: At this time, row and cell selections are not preserved across dynamicData page views. All selections are purged before requests are sent for new sort or pagination states. Selection preservation for these cases is not provided but should be achieved through custom code.

Often, server-side pagination relies on the server response to know the number of total results. By defining a totalRecords locator in your DataSource schema's metaFields, you can access this value to update DataTable's state at runtime before the response data gets loaded by customizing the handleDataReturnPayload() method.

  1. var myDataSource = new YAHOO.util.XHRDataSource("http://myserver.com?");
  2. myDataSource.responseSchema = {
  3. resultsList: "Response.results",
  4. fields: [ "name", "type" ],
  5. metaFields: { totalRecords: "Response.totalRecords" } // Access server-provided dynamic value
  6. };
  7.  
  8. // Enable sorting for every Column
  9. var myColumnDefs = [
  10. { key: "id", sortable: true },
  11. { key: "name", sortable: true },
  12. { key: "description", sortable: true }
  13. ];
  14.  
  15. var myConfigs = {
  16. // Set up pagination
  17. paginator : new YAHOO.widget.Paginator({
  18. rowsPerPage : 50
  19. }),
  20. // Set up initial sort state
  21. sortedBy: {
  22. key: "id",
  23. dir: YAHOO.widget.Datatable.CLASS_ASC
  24. },
  25. // Sorting and pagination will be routed to the server via generateRequest
  26. dynamicData: true
  27. };
  28.  
  29. var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, myConfigs);
  30.  
  31. // Update totalRecords on the fly with value from server
  32. myDataTable.handleDataReturnPayload = function (oRequest, oResponse, oPayload) {
  33. oPayload.totalRecords = oResponse.meta.totalRecords;
  34. return oPayload;
  35. }
  36.  
var myDataSource = new YAHOO.util.XHRDataSource("http://myserver.com?");
myDataSource.responseSchema = {
    resultsList: "Response.results",
    fields: [ "name", "type" ],
    metaFields: { totalRecords: "Response.totalRecords" } // Access server-provided dynamic value
};
 
// Enable sorting for every Column
var myColumnDefs = [
    { key: "id", sortable: true },
    { key: "name", sortable: true },
    { key: "description", sortable: true }
];
 
var myConfigs = {
    // Set up pagination
    paginator : new YAHOO.widget.Paginator({
        rowsPerPage : 50
    }),
    // Set up initial sort state
    sortedBy: {
        key: "id",
        dir: YAHOO.widget.Datatable.CLASS_ASC
    },
    // Sorting and pagination will be routed to the server via generateRequest
    dynamicData: true
};
 
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource, myConfigs);
 
// Update totalRecords on the fly with value from server
myDataTable.handleDataReturnPayload = function (oRequest, oResponse, oPayload) {
    oPayload.totalRecords = oResponse.meta.totalRecords;
    return oPayload;
}
 

When dynamicData is enabled, sorting or paginating will trigger a DataSource request for new data to reflect the state. By default, the request is formatted with the following syntax:

"sort={SortColumnKey}&dir={SortColumnDir}&startIndex={PaginationStartIndex}&results={PaginationRowsPerPage}"

The request that gets sent for each new state can be easily customized by defining a custom generateRequest function (see above).

Security Considerations

When providing HTML values to YUI APIs, implementers need to ensure these strings -- especially when containing data from third-party sources or user input data -- have been properly treated so they may safely be inserted into the DOM. The YAHOO.lang.escapeHTML() method is available for this purpose in version 2.9.0. The built-in "text" formatter (YAHOO.widget.DataTable.formatText) will HTML-escape all string values passed to it before rendering into the DataTable's DOM.

Custom Events, "doBefore" Abstract Methods, and "onEvent" Built-in Event Handlers

The DataTable control provides a robust Custom Event model to allow you to seamlessly integrate and expand upon its built-in fuctionality. Using Custom Events make it easy to react to user interactions like a cell click or a row click:

  1. myDataTable.subscribe("cellClickEvent", function (oArgs) {
  2. var target = oArgs.target,
  3. record = this.getRecord(target),
  4. column = this.getColumn(target);
  5.  
  6. switch (column.key) {
  7. // Do stuff here
  8. }
  9. });
  10.  
myDataTable.subscribe("cellClickEvent", function (oArgs) {
    var target = oArgs.target,
        record = this.getRecord(target),
        column = this.getColumn(target);
 
    switch (column.key) {
        // Do stuff here
    }
});
 

Another example is after you enable inline cell editing (see Inline Cell Editing), you may want to provide a confirmation to your users in reaction to a successful edit event. Note that the newData value passed to your handler function is the value of the form input field, so in the case of a textbox editor, even if the user has entered a number, the type of the input value will be of type String.

  1. // Create a Custom Event handler
  2. var myHandler = function(oArgs) {
  3. var oEditor = oArgs.editor,
  4. newData = oArgs.newData,
  5. oldData = oArgs.oldData;
  6.  
  7. // Output a message to a DIV
  8. YAHOO.util.Dom.get("outputDiv").innerHTML =
  9. "Cell \"" + oEditor.getTdEl().id + //The edited cell element
  10. "\" was successfully updated from \"" + oArgs.oldData + // oArgs.oldData refers to the old value
  11. "\" to \"" + oArgs.newData + // oArgs.newData refers to the new value
  12. "\" for DataTable \"" + this.toString() + "\"."; // this refers to the DataTable instance
  13. };
  14.  
  15. // Assign the handler to the Custom Event
  16. myDataTable.subscribe("editorSaveEvent", myHandler);
  17.  
// Create a Custom Event handler
var myHandler = function(oArgs) {
    var oEditor = oArgs.editor,
        newData = oArgs.newData,
        oldData = oArgs.oldData;
 
    // Output a message to a DIV
    YAHOO.util.Dom.get("outputDiv").innerHTML =
            "Cell \"" + oEditor.getTdEl().id + //The edited cell element
            "\" was successfully updated from \"" + oArgs.oldData + // oArgs.oldData refers to the old value
            "\" to \"" + oArgs.newData + // oArgs.newData refers to the new value
            "\" for DataTable \"" + this.toString() + "\"."; // this refers to the DataTable instance
};
 
// Assign the handler to the Custom Event
myDataTable.subscribe("editorSaveEvent", myHandler);
 

Please refer to the API documentation for a full list of Custom Events that are available for the DataTable control.

The doBeforeSortColumn() method gives implementers a hook to update the UI while sorting occurs.

The doBeforeShowCellEditor() method gives implementers access to the CellEditor instance before it is shown for a edit interaction.

The doBeforeLoadData() method gives implementers access to the DataSource response before it is consumed by the DataTable instance.

DataTable provides a set of built-in "onEvent" functions that are designed to integrate core functionality with user-driven events. Implementers can assign these functions as handlers to DataTable Custom Events to easily build a rich interactive experience.

  1. // Assign built-in handlers to add functionality
  2. myDataTable.subscribe("rowMouseoverEvent", myDataTable.onEventHighlightRow);
  3. myDataTable.subscribe("rowMouseoutEvent", myDataTable.onEventUnhighlightRow);
  4. myDataTable.subscribe("cellClickEvent", myDataTable.onEventShowCellEditor);
  5.  
// Assign built-in handlers to add functionality
myDataTable.subscribe("rowMouseoverEvent", myDataTable.onEventHighlightRow);
myDataTable.subscribe("rowMouseoutEvent", myDataTable.onEventUnhighlightRow);
myDataTable.subscribe("cellClickEvent", myDataTable.onEventShowCellEditor);
 

Skinning DataTable

DataTable comes with a default presentation or "skin," part of the "Sam Skin" visual treatment that accompanies most YUI controls. You can read more about the general approach to skinning YUI components in this in-depth article.

The CSS provided with DataTable is comprised of core, functional CSS as well as the Sam Skin visual treatment.

The DataTable Control

To explore the CSS which controls the DataTable's presentation, please review the DataTable Skinning Example where the full CSS for the control is displayed.

Top Known Issues

Please see the bug repository at YUILibrary.com for a complete list of known issues.

Draggable Columns Require "yui-skin-sam" Class on body Element

When draggable Columns are enabled and the "yui-skin-sam" class is not applied to the body element, the vertical line that indicates the drop target for the Column is not visible. The following custom CSS can be applied as a workaround for implementations that do not wish to apply the "yui-skin-sam" class on the body element.

  1. .yui-dt-coltarget {
  2. width: 5px;
  3. background-color: red;
  4. }
  5.  
.yui-dt-coltarget {
    width: 5px;
    background-color: red;
}
 

Sub-optimal Performance for hideColumn() and showColumn() on IE<8

For DataTables bigger than a certain size, users on IE versions less than 8 may see performance issues when showing or hiding multiple Columns, as described here.

Issues When DataTable or Ancestor is Hidden

  • Widths are not correctly validated. Implementers should call the method onShow() after removing display = "none" on DataTable or ancestor to make sure widths are correctly validated when the widget is unhidden.
  • Borders remain visible in IE. Due to an IE bug, any TABLE element with its border-collapse CSS property set to "collapse", will keep its borders visible even while its contents will be hidden correctly.

Wide Content Truncation in Gecko Browsers

Gecko browsers may truncate some content of Columns whose widths are not specified if a table's overall content is wider than the viewport or its parent container.

IE Quirks Mode Column Widths

In IE quirks mode, Columns without specified widths are not always auto-sized correctly.

Table Caption Incompatibilies

In Opera, table captions interfere with the positioning algorithms needed for Column resizing.

Captions are not supported in ScrollingDataTables.

Opera performance issues with inline cell editing when scrolling is enabled

Opera users may see slow updates when inline cell editing while scrolling is enabled.

Opera Column Inconsistencies With ScrollingDataTable

In Opera, ScrollingDataTables may experience truncated Column content when widths are not specified in the definition.

Arrow Selection Not Supported in Safari

Due to a known limitation in Safari, arrow selection of rows and/or cells is not supported at this time.

YUI on Mobile: Using DataTable Control with "A-Grade" Mobile Browsers

About this Section: YUI generally works well with mobile browsers that are based on A-Grade browser foundations. For example, Nokia's N-series phones, including the N95, use a browser based on Webkit — the same foundation shared by Apple's Safari browser, which is found on the iPhone. The fundamental challenges in developing for this emerging class of full, A-Grade-derived browsers on handheld devices are:

  • Screen size: You have a much smaller canvas;
  • Input devices: Mobile devices generally do not have mouse input, and therefore are missing some or all mouse events (like mouseover);
  • Processor power: Mobile devices have slower processors that can more easily be saturated by JavaScript and DOM interactions — and processor usage affects things like battery life in ways that don't have analogues in desktop browsers;
  • Latency: Most mobile devices have a much higher latency on the network than do terrestrially networked PCs; this can make pages with many script, css or other types of external files load much more slowly.

There are other considerations, many of them device/browser specific (for example, current versions of the iPhone's Safari browser do not support Flash). The goal of these sections on YUI User's Guides is to provide you some preliminary insights about how specific components perform on this emerging class of mobile devices. Although we have not done exhaustive testing, and although these browsers are revving quickly and present a moving target, our goal is to provide some early, provisional advice to help you get started as you contemplate how your YUI-based application will render in the mobile world.

More Information:

The core functionality of the DataTable control operates without any major issues on high-end mobile platforms. Implementers should keep in mind the impact that limited real estate may have on end users of the DataTable and be aware of the following preliminary list of smart phone known issues:

  • Meta-key multi-selection is not supported.
  • Scrolling is not supported on Android devices and requires two-finger scroll on iOS.
  • Resizeable Columns are not supported.
  • Inline cell editing is not fully supported across all platforms and should be well tested in your application. Specifically, input via textbox is not supported on the Nokia N95, and radio buttons and textbox input validation are not supported on the iPhone.
  • XML is not currently supported on Android devices.

Support & Community

The YUI Library and related topics are discussed on the on the YUILibrary.com forums.

Also be sure to check out YUIBlog for updates and articles about the YUI Library written by the library's developers.

Filing Bugs & Feature Requests

The YUI Library's public bug tracking and feature request repositories are located on the YUILibrary.com site. Before filing new feature requests or bug reports, please review our reporting guidelines.

Cheat Sheet for the DataTable Control:

Cheat Sheet for the DataTable Control.

Download full set of cheat sheets.

YUI DataTable on del.icio.us:

Copyright © 2013 Yahoo! Inc. All rights reserved.

Privacy Policy - Copyright Policy - Job Openings