168N/A border: 1px solid #d0d5ec;
168N/A .yui3-datatable table {
168N/A .yui3-datatable td, .yui3-datatable th {
292N/A .yui3-datatable-col-Module {
168N/A .yui3-skin-sam .yui3-datatable-message-content {
168N/A border: 1px solid #eac9a9;
168N/A -moz-border-radius: 3px;
168N/A -webkit-border-radius: 3px;
168N/A -moz-box-shadow: 0 0 5px #ccc8b3;
670N/A -webkit-box-shadow: 0 0 5px #ccc8b3;
769N/A box-shadow: 0 0 5px #ccc8b3;
670N/A<div class="intro component">
670N/A The DataTable widget is responsible for rendering columnar data into a
526N/A highly customizable and fully accessible HTML table. The core
526N/A functionality of DataTable is to visualize structured data as a table.
526N/A A variety of class extensions can then be used to add features to the
168N/A table such as sorting and scrolling.
168N/A <h2 id="migration-intro">Upgrading from version 3.4.1 or older?</h2>
613N/A DataTable was refactored for 3.5.0. Some APIs were changed in backward
613N/A avoid unpleasant surprises. If you still run into issues, please
168N/A If you are unable to upgrade due to unresolvable issues, you can use the
670N/A module suite, which is equivalent to the 3.4.1 implementation. But be
670N/A aware that these modules will be removed in a future version of YUI.
670N/A<h2 id="using">DataTable Basics</h2>
670N/A A basic DataTable is made of columns and rows. Define the columns you
670N/A want to display in your DataTable with the `columns` attribute. Rows are
670N/A created for you based on the data you provide to the `data` attribute.
670N/A Under the hood, the DataTable class uses a
670N/A data properties. Read the <a href="#data">Table Data Configuration</a>
670N/A section below for details about how to load, configure, and work with the
670N/A// Columns must match data object property names
670N/A { id: "ga-3475", name: "gadget", price: "$6.99", cost: "$5.99" },
670N/A { id: "sp-9980", name: "sprocket", price: "$3.75", cost: "$3.25" },
670N/A { id: "wi-0650", name: "widget", price: "$4.25", cost: "$3.75" }
670N/A columns: ["id", "name", "price"],
670N/A // Optionally configure your table with a caption
670N/A caption: "My first DataTable!",
670N/A summary: "Example DataTable showing basic instantiation configuration"
670N/A<p>This code produces this table:</p>
670N/A<div id="basic-example" class="yui3-skin-sam"></div>
670N/AYUI({ filter: 'raw' }).use('datatable-base', function (Y) {
670N/A // Columns must match data object property names
670N/A { id: "ga-3475", name: "gadget", price: "$6.99", cost: "$5.99" },
670N/A { id: "sp-9980", name: "sprocket", price: "$3.75", cost: "$3.25" },
168N/A { id: "wi-0650", name: "widget", price: "$4.25", cost: "$3.75" }
168N/A columns: ["id", "name", "price"],
670N/A caption: "My first DataTable!",
670N/A summary: "Example DataTable showing basic instantiation configuration"
295N/A<h2 id="columns">Column Configuration</h2>
295N/A The `columns` attribute takes an array of field names that correspond to
295N/A property names in the `data` objects. These field names are called "keys".
295N/A As long as these keys exist in your data, DataTable will display the
295N/A values in the table. By default, the key is also used as the label of the
295N/A Use objects instead of key strings to customize how the cells in a column
769N/A// Columns must match data object property names
295N/A { id: "ga-3475", name: "gadget", price: "$6.99", cost: "$5.99" },
769N/A { id: "sp-9980", name: "sprocket", price: "$3.75", cost: "$3.25" },
670N/A { id: "wi-0650", name: "widget", /* missing */ cost: "$3.75" }
670N/A { key: "name", label: "part name" },
670N/A { key: "price", allowHTML: true, emptyCellValue: "<em>(not set)</em>" },
295N/A<p>This code produces this table:</p>
295N/A<div id="column-example1" class="yui3-skin-sam"></div>
295N/AYUI().use('datatable-base', function (Y) {
295N/A // Columns must match data object property names
295N/A { id: "ga-3475", name: "gadget", price: "$6.99", cost: "$5.99" },
295N/A { id: "sp-9980", name: "sprocket", price: "$3.75", cost: "$3.25" },
295N/A { id: "wi-0650", name: "widget", cost: "$3.75" }
670N/A { key: "name", label: "part name" },
670N/A { key: "price", allowHTML: true, emptyCellValue: "<em>(not set)</em>" },
295N/A Some column configurations affect the table headers and others affect the
295N/A Use the `key` property to reference the associated data field when
295N/A configuring columns with objects. Other supported configuration
295N/A properties are listed in <a href="#column-config">Appendix A</a> below.
295N/A<h3 id="nested">Stacked Column Headers</h3>
670N/A Use the `children` column configuration to create multiple rows of column
670N/A // Important: Parent columns do NOT get a key...
670N/A // Pass an array of column configurations (strings or objects) as children
295N/A { username: "root", read: true, write: true },
670N/A { username: "spilgrim", read: true, write: false },
670N/A { username: "fizzgig", read: false, write: false }
670N/A<p>This code produces this table:</p>
670N/A<div id="nested-example" class="yui3-skin-sam"></div>
670N/AYUI().use('datatable-base', function (Y) {
670N/A { username: "root", read: true, write: true },
670N/A { username: "spilgrim", read: true, write: false },
670N/A { username: "fizzgig", read: false, write: false }
670N/A // Important: Parent columns do NOT get a key...
670N/A // Pass an array of column configurations (strings or objects) as children
670N/A}).render("#nested-example");
670N/A `children` takes an array of column configurations, just like the `columns`
670N/A attribute itself. The columns defined in the `children` property will have
670N/A header cells rendered below the parent column's header.
670N/A Columns that have `children` don't relate directly to the data cells in the
670N/A table rows, so they <strong>should not</strong> have a `key` configured.
670N/A They should, however, include a `label` to provide the header's content.
670N/A<h3 id="formatters">Formatting Cell Data</h3>
670N/A To customize the display of cell data in a column, DataTable provides the
670N/A `formatter` and `nodeFormatter` column configurations. Both configurations
670N/A accept functions, but `formatter` will also accept a template string.
670N/A `formatter`s are expected to return the string content to populate each
670N/A cell in that column, and `nodeFormatter`s are provided with the cell Nodes
670N/A and expected to populate them using the Node API.
168N/A For best performance, <strong><a href="#formatter-vs-nodeformatter">avoid
168N/A `nodeFormatter`s unless absolutely necessary</a></strong>.
670N/A formatter: '${value}' // formatter template string
670N/A formatter: function (o) {
168N/A nodeFormatter: function (o) {
168N/A // Assign a rowspan to the first cell and add a new row
670N/A // below this one to span the last three columns
168N/A '<tr class="auth"><td colspan="3">' +
168N/A '<button class="ok">authorize</button>' +
670N/A '<button class="stop">discontinue</button>' +
168N/A<p>This code produces this table:</p>
168N/A<div id="formatter-example" class="yui3-skin-sam">
168N/A .yui3-datatable .yui3-datatable-data .expensive {
168N/A background-color: #ffe;
769N/A .yui3-skin-sam .yui3-datatable-data .auth td {
670N/A border-bottom: 1px dashed #cbcbcb;
769N/A background-color: #fff;
670N/A border-top: 1px dashed #cbcbcb;
670N/A border-left: 1px solid #cbcbcb;
168N/AYUI().use('datatable-base', function (Y) {
670N/A formatter: '${value}' // formatter template string
670N/A formatter: function (o) {
168N/A nodeFormatter: function (o) {
670N/A '<tr class="auth"><td colspan="3">' +
613N/A '<button class="ok">authorize</button>' +
168N/A '<button class="stop">discontinue</button>' +
168N/A { item: "widget", cost: 23.57, price: 47.5 },
168N/A { item: "gadget", cost: 0.11, price: 6.99 },
168N/A { item: "sprocket", cost: 4.08, price: 3.75 },
168N/A { item: "nut", cost: 0.01, price: 0.25 }
168N/A }).render("#formatter-example");
613N/A The parameters passed to `formatter` functions and `nodeFormatter`
613N/A functions are described in <a href="#formatter-props">Appendix B</a> and <a
670N/A href="#nodeformatter-props">Appendix C</a>, respectively.
168N/A <strong>Note</strong>: It's highly recommended to keep the data in the
168N/A underlying `data` ModelList as pure data, free from presentational
670N/A concerns. For example, use real numbers, not numeric strings, and store
670N/A link urls and labels either in separate data fields or in a single data
670N/A field, but as separate properties of a value object. This allows the data
670N/A to be used for calculations such as sorting or averaging.
670N/A<h4 id="formatter-function">Setting content with `formatter` functions</h4>
670N/A Set the cell content with column `formatter`s by returning the desired
670N/A content string from the function. Alternately, just update `
o.value` with
168N/A the new value in the object passed as an argument to the `formatter`. When
168N/A updating `
o.value` <em>do not include a return statement</em>.
670N/A `formatters` are very powerful because not only do they have access to the
168N/A record's value for that column's field, but they also receive the rest of
168N/A the record's data, the record Model instance itself, and the column
670N/A configuration object. This allows you to include any extra configurations
168N/A in your column configuration that might be useful to customizing how cells
168N/A in the column are rendered.
670N/A { key: "price", formatter: currency, decimalPlaces: 3 },
168N/A See <a href="#formatter-props">Appendix B</a> for a list of all properties
168N/A passed to `formatter` functions.
670N/A<h4 id="nodeformatters">Setting content with `nodeFormatter` functions</h4>
670N/A Unlike `formatters` which can effectively default to the normal rendering
670N/A logic by leaving `
o.value` unchanged, `nodeFormatters` must assign content
295N/A to the cells themselves. The cell's initial classes will be set up, but
670N/A that's it. Everything else is your responsibility.
670N/A <strong>`nodeFormatter`s should return `false`</strong>.
670N/A <a href="formatter-vs-nodeformatter">See below</a> for details.
670N/A While there are <a href="#formatter-vs-nodeformatter">few scenarios that
670N/A require `nodeFormatter`s</a>, they do have the benefits of having the Node
670N/A API for constructing more complex DOM subtrees and the ability to access
670N/A all nodes in the `<tbody>`. This means they can reference, and even modify,
670N/A Like `formatters`, `nodeFormatters` are provided with the data field value,
670N/A the record data, the record Model instance, and the column configuration
670N/A See <a href="#nodeformatter-props">Appendix C</a> for a list of all
670N/A properties passed to `nodeFormatter` functions.
670N/A<h4 id="formatter-vs-nodeformatter">Why `formatter` and `nodeFormatter`?</h4>
670N/A For good rendering performance and memory management, DataTable creates
670N/A table content by assembling `innerHTML` strings from templates, with
670N/A `{placeholder}` tokens replaced with your data. However, this means that
670N/A the Nodes don't exist yet when a column's `formatter`s are applied.
295N/A To minimize the need to create Nodes for each cell, the default rendering
670N/A logic supports the addition of cell classes as well as row classes via
670N/A `formatter` functions. Event subscriptions should be
670N/A from the DataTable instance itself using the
168N/A On the rare occasion that you <em>must</em> use Nodes to supply the cell
613N/A data, DataTable allows a second pass over the generated DOM elements once
613N/A the initial string concatenation has been completed and the full HTML
168N/A It is important to note that `nodeFormatters` will necessarily create a
168N/A Node instance for each cell in that column, which will increase the memory
168N/A footprint of your application. If the Node instance wrappers around the
168N/A DOM elements don't need to be maintained beyond the life of the
168N/A `nodeFormatter`, return `false` to remove them from the internal object
613N/A cache. <strong>This will not remove the rendered DOM, but it will remove
613N/A event subscriptions made on those Nodes</strong>.
670N/A In general, `nodeFormatter`s should only be used if absolutely necessary,
168N/A and should <em>always return `false`</em>.
670N/A<h4 id="formatters-vs-empty">Formatters vs. `emptyCellValue`</h4>
168N/A The `emptyCellValue` configuration is useful to provide fallback content in
168N/A the case of missing or empty column data, but it interacts with each type of
168N/A String formatters will only be applied if the field data for that cell is
613N/A not `undefined`. This allows the `emptyCellValue` to populate the cell.
613N/A Function formatters are applied before the return value or (potentially
168N/A altered) `
o.value` property is tested for `undefined`, `null`, or the empty
295N/A string. In any of these cases, the `emptyCellValue` populates the cell.
168N/A The `emptyCellValue` configuration is ignored by columns configured with
613N/A<h2 id="data">Table Data Configuration</h2>
168N/A Each record in the table is stored as a
168N/A keys of the record objects become Model attributes. This allows you to
613N/A interact with the models as you would any other <a
670N/A `set(attr, value)`, and subscribing to attribute change events.
168N/A { item: "widget", cost: 23.57, price: 47.5 },
168N/A { item: "gadget", cost: 0.11, price: 6.99 },
168N/A { item: "sprocket", cost: 4.08, price: 3.75 },
168N/A { item: "nut", cost: 0.01, price: 0.25 }
columns: ["item", "cost", "price"],
// Fires a costChange event, and the table is updated if rendered
The Model class used to store the record data is created for you, based on
the objects in the `data` array. If `data` is not set, the column keys
identified in the `columns` configuration is used.
<h3 id="recordtype">Specifying the Record Model</h3>
To use a custom Model for your records, pass your Model subclass to the
columns: ['slices', 'type'],
//
Y.PieModel has attributes 'slices', which defaults to 6, and 'type',
// which defaults to 'apple'. Records can use these defaults.
{ type: 'lemon meringue' },
{ type: 'chocolate creme', slices: 8 },
{} // equivalent to { type: 'apple', slices: 6 }
//
Y.PieModel has its idAttribute assigned to 'type', overriding the default
// of 'id'. Fetch a PieModel by its id.
// eatSlice is a method on the
Y.PieModel prototype
Alternately, `recordType` will accept an array of attribute strings or an
`ATTRS` configuration object to make it easier to create custom attribute
behaviors without needing to explicitly build the Model subclass.
If the `columns` configuration is omitted, but the `recordType` is set, the
`columns` will default to the `recordType`'s attributes.
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
{ item: "sprocket", cost: 4.08, price: 3.75 },
{ item: "nut", cost: 0.01, price: 0.25 }
// Effectively synonymous with setting the columns attribute if no special
// column configuration is needed.
recordType: [ 'item', 'cost', 'price' ],
// Or for more control, pass an ATTRS configuration object
setter: function (val) { return +val || 0; }
valueFn: function () { return (
this.get('cost') + 0.1) * 10; },
setter: function (val) { return +val || 0; }
When the table data is loaded asychronously, it is often a good idea to
configure the `recordType`. This can prevent the generation of a record
Model that is missing fields that are omitted from the `columns`
configuration because they aren't intended for viewing.
<h3 id="modellist">The `data` ModelList</h3>
The record Models are stored in a
`data` <em>property</em> on the instance (for easier access than going through `
table.get('data')`).
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
{ item: "sprocket", cost: 4.08, price: 3.75 }
columns: ["item", "cost", "price"],
// Add a new Model using the ModelList API. This will fire
// events and change the table if rendered.
When assigning the DataTable's `data` attribute with an array, a ModelList
is created for you. But you can also pass a ModelList instance if you are
sharing a ModelList between widgets on the page, or you have created custom
Model and ModelList classes with additional logic, such as adding a
<a href="../model-list/#implementing-a-list-sync-layer">data sync layer</a>.
columns: ['type', 'slices'],
// The
Y.PieList class implements a sync layer, enabling its load() method
<h3 id="getting-data">Getting Remote Table Data</h3>
To fetch remote data, you have three options:
<strong>For quick one-offs</strong>, you can load and parse the
data manually, using `
Y.io(...)`, `
Y.jsonp(...)`, etc., then assign
that data to the DataTable's `data` attribute. This isn't very
elegant or maintainable, so is best avoided for anything other than
<strong>For the most control</strong>, better maintainability, and
better encapsulation of business logic, create Model and ModelList
<a href="../model-list/#implementing-a-list-sync-layer">implement a
sync layer</a> as suggested above.
<strong>For common read-only scenarios</strong>, use the
plugin to bind your table to a
plugins to add DataSource features.
// Create a JSONP DataSource to query YQL
// Important that record fields NOT include ".", so
// extract nested data with locators
// No data is provided at construction because it will load via the
// DataTableDataSource plugin
columns: ['Title', 'Phone', 'Rating'],
summary: 'Pizza places near 98089'
// Initially render an empty table and show a loading message
.showMessage('loadingMessage');
// Load the data into the table
request: encodeURIComponent(
<h2 id="features">DataTable Modules and Features</h2>
For a basic, stripped down `
Y.DataTable` class, include the `datatable-base`
Feature modules, such as `datatable-sort`, will bring in `datatable-base`
automatically. By including only feature modules in your `use()`, you will
get a `
Y.DataTable` that supports specifically those features, without
extra code for other features you won't be using.
The `datatable` module is a bundle of `datatable-base` plus a set of common
feature modules. Other feature modules need to be included explicitly in
The core API for DataTable, implemented as a class extension, used
`
Y.View`. DataTable defers rendering of the `<thead>` content to
this View when it is passed as the DataTable's `headerView`
attribute (the default, as set by `datatable-base`).
`
Y.View`. DataTable defers rendering of the `<tbody>` content to
this View when it is passed as the DataTable's `bodyView`
attribute (the default, as set by `datatable-base`).
`showMessage` and `hideMessage` methods to `
Y.DataTable`.
support for the `width` property in column configuration objects
such as `addRow`, `removeRow`, and `moveColumn` to `
Y.DataTable`.
`sort` and `toggleSort` as well as attributes `sortable` and
`sortBy` to `
Y.DataTable`. Enables sorting the table rows by
clicking on column headers.
DataSource instance to the table as its source of record data.
horizontally scrolling table rows within fixed table dimensions.
By including only `datatable-base` in your `use()` line, you get both
inclusion, these classes are effectively the same. When additional
DataTable related modules are included, those modules' features will
usually be added to `
Y.DataTable`, but <strong>never</strong> to
primarily as a superclass to a custom DataTable implementation that has a
locked set of features that will not be modified, as `
Y.DataTable` can be,
by the inclusion of other modules.
// Create a custom DataTable that includes only the core set of APIs, plus
// sorting and message support.
Y.use('datatable-scroll', function (Y) {
basic table attributes, the underlying ModelList and View rendering
architecture, as well as methods to fetch rows and cells or columns and
Rendering features include most column configurations, such as `children`
and `allowHTML`, as well as column formatting options `formatter`,
`nodeFormatter`, `cellTemplate`, etc.
<h3 id="datatable-message">Table Messages</h3>
The `datatable-message` module adds the ability to display a message in the
table body. By default, the "emptyMessage" will display when the table's
ModelList has no data records. The message will hide when data is added.
columns: ["id", "name", "price"],
<p>This code produces this table:</p>
<div id="message-example" class="yui3-skin-sam"></div>
YUI({ filter: 'raw' }).use('datatable-message', function (Y) {
columns: ["id", "name", "price"],
}).render('#message-example');
`showMessage` supports internationalized strings by using a few named
strings, which are registered in the language packs for the
`datatable-message` module . These strings are currently:
Other values passed to `showMessage` will pass that content directly
through to the message Node.
<h3 id="colwidths">Column Width Configuration</h3>
The `datatable-column-widths` module adds basic support for specifying
{ key: 'item', width: '125px' },
{ key: 'cost', formatter: '${value}' },
<p>This code produces this table:</p>
<div id="colwidths-example" class="yui3-skin-sam"></div>
YUI({ filter: 'raw' }).use('datatable-column-widths', function (Y) {
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
{ item: "sprocket", cost: 4.08, price: 3.75 },
{ item: "nut", cost: 0.01, price: 0.25 }
{ key: 'item', width: '125px' },
{ key: 'cost', formatter: '${value}' },
{ key: 'price', formatter: '${value}' },
{ key: 'profit', formatter: function (o) {
return (((price - cost) / cost) * 100).toFixed(2) + '%';
}).render("#colwidths-example");
<strong>CAVEAT</strong>: Column widths will expand beyond the configured
value if column cells contain data that is long and can't line-wrap. Also,
column widths may be reduced below the configured value if the table width
(by configuring the DataTable's `width` attribute, or constrained by a
narrow containing element) is too narrow to fit all data at the configured
To force column widths, including cell data truncation and allowing the
table to spill beyond its configured or inherited width, wrap the cell
content in a `<div>` either by configuring the column's `formatter` or
`cellTemplate`, then assign the `<div>`'s CSS style with the desired width
(or "inherit"), plus `overflow: hidden;`. Then set the DataTable column's
`width` configuration accordingly.
<h3 id="sorting">Column sorting</h3>
The `datatable-sort` module adds support for sorting the table rows either
through the added APIs or by clicking on the table headers.
By default, when `datatable-sort` is included, DataTables will inspects
the `columns` objects, looking for `sortable: true` to enable table sorting
by those columns, triggered by clicking on their respective headers.
{ key: "Company", sortable: true },
{ key: "Contact", sortable: true }
For convenience, you can enable header-click sorting for all columns by
setting the `sortable` attribute to `true`, or pass an array of column keys
to enable just those column's headers.
// Set all columns to be sortable
columns: ["Company", "Phone", "Contact"],
<p>This code produces this table:</p>
<div id="sort-example1" class="yui3-skin-sam"></div>
YUI({ filter: 'raw' }).use('datatable-sort', function (Y) {
columns: ['Company', 'Phone', 'Contact'],
{ Company: "Company Bee", Phone: "415-555-1234", Contact: "Sally Spencer"},
{ Company: "Acme Company", Phone: "650-555-4444", Contact: "John Jones"},
{ Company: "Indutrial Industries", Phone: "408-555-5678", Contact: "Robin Smith"}
}).render('#sort-example1');
Hold down the `shift` key while clicking on column headers to subsort by
that column. Doing so repeatedly will toggle the subsort direction.
As long as the `datatable-sort` module has been included, you will always
be able to sort the table data through the API, even by columns that aren't
configured to accept header-click sorting.
When a table is sorted, any new records added to the DataTable's ModelList
will be inserted at the proper sorted index, as will the created table
Disable header-click sorting by setting `sortable` to `false`.
<h4 id="customsort">Custom Sorting</h4>
Assign a function to a column's `sortFn` to support customized sorting. The
function will receive the two records being compared and a boolean flag
indicating a descending sort was requested.
label: '●', // a big dot
formatter: function (o) {
return
o.value ? '' : '●'; // only new records have a dot
sortFn: function (a, b, desc) {
acid =
a.get('clientId'),
bcid =
b.get('clientId'),
order = // existing records are equivalent
// new records are grouped apart from existing records
(aid && -1) || (bid && 1) ||
// new records are sorted by insertion order
(acid > bcid) ? 1 : -(acid < bcid);
return desc ? -order : order;
The function must return 1, 0, or -1. 1 specifies that the Model passed as
the first parameter should sort below the Model passed as the second
parameter. -1 for above, and 0 if they are equivalent for the purpose of
<h4 id="sortapi">Sorting Methods</h4>
To sort the table in the code, call `
table.sort(NAME OR KEY)`. To
// Sorts the table by values in the price field in ascending order
To sort by multiple columns, pass an array of column keys to `sort` or
Calling `toggleSort` with no arguments will reverse all current sort
directions. Calling with specific column names or keys will toggle only
// Sort first by author, subsort by title in ascending order
// Now descending by author then title
// Now ascending by author, descending by title
To specify a sort direction, pass an object instead of a string to `sort`.
The object should have the column name as the key, and sort direction as its
// Explicitly sort by price in descending order
// Each column gets its own object
table.sort([{ author: 'desc' }, { title: 'desc' }]);
Acceptable values for the sort direction are "asc", "desc", 1, and -1. 1 is
equivalent to "asc", and -1 to "desc".
<h4 id="sortby">The `sortBy` Attribute</h4>
Every sort operation updates the `sortBy` attribute. You can also trigger
a sort by setting this attribute directly. It accepts the same values as
// Sort by author in descending order, then by title in ascending order
table.set('sortBy', [{ author: -1 }, 'title']);
To specify an initial sort order for your table, assign this attribute
during instantiation. This will sort the data as soon as it is added
to the table's ModelList.
columns: ['item', 'cost', 'price'],
<h4 id="sortevent">The `sort` Event</h4>
Clicking on a column header, or calling the `sort` or `toggleSort` methods
will fire a `sort` method containing an `
e.sortBy` property that
corresponds to the requested sort column and direction. The value will be
in either string or object format, depending on how each method was used.
Preventing the `sort` event will prevent the `sortBy` attribute from being
updated. Updating the `sortBy` attribute directly will not fire the `sort`
event, but will still sort the data and update the table.
<h3 id="mutation">Table Mutation APIs (`addRow`, etc)</h3>
The `datatable-mutable` module adds APIs for adding, removing, and
modifying records and columns.
<h4 id="column-mutation">Column Mutation Methods</h4>
Use the methods `addColumn`, `removeColumn`, `modifyColumn`, and
`moveColumn` to update the table's configured `columns`.
// Insert a column for the profit field in the data records as the third column
// Actually, make that the fourth column
// Actually, strike that. Don't show it after all
// Instead, add a formatter to the price column that includes the profit data
formatter: function (o) {
Each column mutation method fires an identically named event. See
<h4 id="row-mutation">Row Mutation Methods</h4>
Use the methods `addRow`, `addRows`, `removeRow`, and `modifyRow` to update
{ item: 'nut', cost: 0.42, price: 2.65 },
{ item: 'washer', cost: 0.01, price: 0.08 },
{ item: 'bit', cost: 0.19, price: 0.97 }
// Remove table records by their Model, id, clientId, or index
// Modify a record by passing its id, clientId, or index, followed by an
// object with new field values
Everything that's done by these methods can be accomplished through the
table's ModelList instance methods, but having methods on the table itself
can make the code more readable.
By default, changes made to the table are only local, they don't update the
server or other data origin if the data was served remotely. However, if
your table's ModelList is built with a sync layer, the mutation methods
can also trigger the appropriate sync behavior by passing an additional
argument to the methods, an object with the property `sync` set to `true`.
// Tell the server we're down to one slice of apple pie!
// Uh oh, make that 0. No more apple pie :(
If all modifications are destined for the
server/origin, you can set the
`autoSync` attribute to `true`, and the row mutation methods will
automatically call into the sync layer.
columns: ['type', 'slices'],
// The new PieModel's save() method is called, notifying the server
<h3 id="scrolling">Scrolling</h3>
<strong>Note:</strong> Scrolling is not currently supported on the Android
Scrolling functionality can be added to `
Y.DataTable` by including
`datatable-scroll` module in your `use()`. `datatable-scroll` is
<strong>NOT</strong> included in the `datatable` rollup module, so must be
Enable scrolling by setting the `scrollable` attribute, which accepts values
"x", "y", "xy", `true` (same as "xy"), or `false` (the default).
Note, vertical scrolling also requires the table's `height` attribute to be
set, and horizontal scrolling requires the `width` to be set.
// Data from the seafoodwatch YQL table as of 3/16/2012
{ "fish": "Barramundi (Imported Farmed in Open Systems)", "recommendation": "avoid" },
{ "fish": "Caviar, Paddlefish (Wild caught from
U.S.)", "recommendation": "avoid" },
{ "fish": "Caviar, Sturgeon (Imported Wild-caught)", "recommendation": "avoid" },
// Enable vertical scrolling with scrollable "y". The width is also set, but
// because scrollable is not "x" or "xy", this just sets the table width.
caption: 'Seafood tips for the US West Coast',
columns: ['fish', 'recommendation'],
<p>This code produces this table:</p>
<div id="scroll-example" class="yui3-skin-sam"></div>
YUI().use('datatable-scroll', function (Y) {
// Data from the seafoodwatch YQL table as of 3/16/2012
{ "fish": "Barramundi (Imported Farmed in Open Systems)", "recommendation": "avoid" },
{ "fish": "Caviar, Paddlefish (Wild caught from
U.S.)", "recommendation": "avoid" },
{ "fish": "Caviar, Sturgeon (Imported Wild-caught)", "recommendation": "avoid" },
{ "fish": "Chilean Seabass", "recommendation": "avoid" },
{ "fish": "Cobia (Imported Farmed)", "recommendation": "avoid" },
{ "fish": "Cod, Atlantic (Trawl-caught from Canadian and
U.S. Atlantic)", "recommendation": "avoid" },
{ "fish": "Cod, Pacific (Imported)", "recommendation": "avoid" },
{ "fish": "Crab, King (Imported)", "recommendation": "avoid" },
{ "fish": "Dab, Common (Danish Seine from Iceland)", "recommendation": "avoid" },
{ "fish": "Flounder (Wild-caught from
U.S. Atlantic, Except Summer Flounder)", "recommendation": "avoid" },
{ "fish": "Hake, White", "recommendation": "avoid" },
{ "fish": "Halibut, Atlantic (Wild-caught from
U.S.)", "recommendation": "avoid" },
{ "fish": "Lobster, Caribbean Spiny (Brazil)", "recommendation": "avoid" },
{ "fish": "Mahi Mahi (Imported Longline)", "recommendation": "avoid" },
{ "fish": "Marlin, Blue (Imported)", "recommendation": "avoid" },
{ "fish": "Marlin, Striped", "recommendation": "avoid" },
{ "fish": "Monkfish", "recommendation": "avoid" },
{ "fish": "Octopus (Philippines)", "recommendation": "avoid" },
{ "fish": "Orange Roughy", "recommendation": "avoid" },
{ "fish": "Plaice, American (Wild-caught from
U.S. Atlantic)", "recommendation": "avoid" },
{ "fish": "Pollock, Atlantic (Danish Seine and Trawl from Iceland)", "recommendation": "avoid" },
{ "fish": "Salmon (Farmed including Atlantic Except
U.S. Farmed in Tank Systems)", "recommendation": "avoid" },
{ "fish": "Sardines, Atlantic (Mediterranean)", "recommendation": "avoid" },
{ "fish": "Shad, American (
U.S. Atlantic Gillnet)", "recommendation": "avoid" },
{ "fish": "Shark (Except Common Thresher and Shortfin Mako, from California and Hawaii)", "recommendation": "avoid" },
{ "fish": "Shrimp (Imported Farmed Except Thailand Farmed in Fully Reciruclating Systems)", "recommendation": "avoid" },
{ "fish": "Shrimp (Imported Wild-Caught Except Canadian)", "recommendation": "avoid" },
{ "fish": "Shrimp (Mexico Farmed in Open Systems)", "recommendation": "avoid" },
{ "fish": "Skates", "recommendation": "avoid" },
{ "fish": "Snapper, Red (
U.S. Gulf of Mexico Wild-caught)", "recommendation": "avoid" },
{ "fish": "Snapper, Red (Imported Wild-caught)", "recommendation": "avoid" },
{ "fish": "Sole (Wild-caught from
U.S Atlantic)", "recommendation": "avoid" },
{ "fish": "Sturgeon (Imported Wild-caught)", "recommendation": "avoid" },
{ "fish": "Swordfish (Imported)", "recommendation": "avoid" }
caption: 'Seafood tips for the US West Coast',
columns: ['fish', 'recommendation'],
width: "400px" // scrollable is not set to x or xy, so no scrolling
}).render("#scroll-example");
<h2 id="events">DataTable Events</h2>
DataTable is a composition of supporting class instances and extensions, so
to centralize event reporting, it is a bubble target for its `data`
ModelList as well as the View instances used for rendering.
In other words, some events you may need to subscribe to using an event
prefix to be notified. Often, using a wildcard prefix is the simplest
method to ensure your subscribers will be notified, even if classes change.
// The sort event is from an extension, so it originates from DataTable
// Model changes originate from the record's Model instance, propagate to the
// table's ModelList, then finally to the DataTable, so they must be
// subscribed with an event prefix. In this case, we'll use a wildcard
DataTable generates a custom Model class with the "record" event prefix, if
you want to be more specific. Otherwise, if your table uses a custom Model
class for its `recordType`, you can prefix Model events with the appropriate
// Allow DataTable to generate the Model class automatically
columns: ['items', 'cost', 'price'],
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
// generated Model classes have prefix "record"
// PieList uses PieModels, which have a prefix of, you guessed it, "pie"
columns: ['type', 'slices'],
pies.on('pie:slicesChange', function (e) {
The full list of events is included in <a href="{{apiDocs}}
/classes/DataTable.html#events">the DataTable API docs</a>.
<h2 id="knownissues">Known Issues</h2>
currently supported on Android</a> WebKit browser.
appear scrollable</a> on iOS browsers or OS X 10.7 depending on the
system preference "Show scroll bars" (General).
<h2 id="column-config">Appendix A: Column Configurations</h2>
The properties below are supported in the column configuration objects
passed in the `columns` attribute array.
<div id="column-config-table" class="yui3-skin-sam">
<th scope="col">Configuration</th>
<th scope="col">Description</th>
<th scope="col">Module</th>
Binds the column values to the named property in the `data`.
Optional if `formatter`, `nodeFormatter`, or `cellTemplate`
is used to populate the content.
<p>It should not be set if `children` is set.</p>
The value is used for the `_id` property unless the `name`
<td>`datatable-base`</td>
{ name: 'fullname', formatter: ... }
or style columns with class "yui3-datatable-col-NAME" if a
column isn't assigned a `key`.
The value is used for the `_id` property.
<td>`datatable-base`</td>
{ field: 'fullname', formatter: ... }
<p>An alias for `name` for backward compatibility.</p>
<td>`datatable-base`</td>
Overrides the default unique id assigned `<th id="HERE">`.
<em>Use this with caution</em>, since it can result in
duplicate ids in the DOM.
<td>`datatable-base`</td>
{ key: 'MfgPrtNum', label: 'Part Number' }
<p>HTML to populate the header `<th>` for the column.</p>
<td>`datatable-base`</td>
Used to create stacked headers.
<a href="#nested">See the example above</a>.
Child columns may also contain `children`. There is no limit
Columns configured with `children` are for display only and
<strong>should not</strong> be configured with a `key`.
Configurations relating to the display of data, such as
`formatter`, `nodeFormatter`, `emptyCellValue`, etc. are
<td>`datatable-base`</td>
label: '1yr Target Forecast',
<p>Assigns the value `<th abbr="HERE">`.</p>
<td>`datatable-base`</td>
for this column. This is necessary when more control is
needed over the markup for the header itself, rather than
Use the `label` configuration if you don't need to
customize the `<th>` iteself.
Implementers are strongly encouraged to preserve at least
the `{id}` and `{_id}` placeholders in the custom value.
<td>`datatable-base`</td>
'<td class="{className}">' +
'<input type="checkbox" ' +
for this column. This is necessary when more control is
needed over the markup for the `<td>` itself, rather than
<td>`datatable-base`</td>
Used to customize the content of the data cells for this
<a href="#formatters">See the example above</a>
<td>`datatable-base`</td>
Used to customize the content of the data cells for this
<a href="#formatters">See the example above</a>
<td>`datatable-base`</td>
Provides the default value to populate the cell if the data
for that cell is `undefined`, `null`, or an empty string.
<td>`datatable-base`</td>
Skips the security step of HTML escaping the value for cells
in this column. This is also necessary if `emptyCellValue`
is set with an HTML string.
`nodeFormatter`s ignore this configuration. If using a
`nodeFormatter`, it is recommended to use
on any user supplied content that is to be displayed.
<td>`datatable-base`</td>
A string of CSS classes that will be added to the `<td>`'s
Note, all cells will automatically have a class in the
form of "yui3-datatable-col-KEY" added to the `<td>`, where
KEY is the column's configured `name`, `key`, or `id` (in
that order of preference).
<td>`datatable-base`</td>
{ key: 'a', width: '400px' },
{ key: 'b', width: '10em' }
Adds a style `width` setting to an associated `<col>`
Note, the assigned width will not truncate cell content, and
it will not preserve the configured width if doing so would
compromise either the instance's `width` configuration or
the natural width of the table's containing DOM elements.
If absolute widths are required, it can be accomplished with
some custom CSS and the use of a `cellTemplate`, or
description of `datatable-column-widths`</a> for an example
<td>`datatable-column-widths`</td>
{ key: 'lastLogin', sortable: true }
Used when the instance's `sortable` attribute is set to
"auto" (the default) to determine which columns will support
user sorting by clicking on the header.
If the instance's `sortable` attribute is set, this
configuration is ignored.
<td>`datatable-sort`</td>
sortFn: function (a, b, desc) {
order = (an > bn) ? 1 : -(an < bn);
return desc ? -order : order;
formatter: function (o) {
Allows a column to be sorted using a custom algorithm. The
function receives three parameters, the first two being the
two record Models to compare, and the third being a boolean
`true` if the sort order should be descending.
The function should return `-1` to sort `a` above `b`, `-1`
to sort `a` below `b`, and `0` if they are equal. Keep in
mind that the order should be reversed when `desc` is
The `desc` parameter is provided to allow `sortFn`s to
always sort certain values above or below others, such as
always sorting `null`s on top.
<td>`datatable-sort`</td>
(<strong>read-only</strong>) If a column is sorted, this
will be set to `1` for ascending order or `-1` for
descending. This configuration is public for inspection,
but can't be used during DataTable instantiation to set the
sort direction of the column. Use the table's
<td>`datatable-sort`</td>
(<strong>read-only</strong>) The unique identifier assigned
to each column. This is used for the `id` if not set, and
the `_id` if none of `name`, 'field`, `key`, or `id` are
<td>`datatable-base`</td>
(<strong>read-only</strong>) A unique-to-this-instance name
used extensively in the rendering process. It is also used
to create the column's classname, as the input name
`<th data-yui3-col-id="HERE">`.
The value is populated by the first of `name`, `field`,
`key`, `id`, or `_yuid` to have a value. If that value
has already been used (such as when multiple columns have
the same `key`), an incrementer is added to the end. For
example, two columns with `key: "id"` will have `_id`s of
<td>`datatable-base`</td>
(<strong>read-only</strong>) Used by
<td>`datatable-base`</td>
(<strong>read-only</strong>) Used by
<td>`datatable-base`</td>
(<strong>read-only</strong>) Assigned to all columns in a
column's `children` collection. References the parent
<td>`datatable-base`</td>
(<strong>read-only</strong>) Array of the `id`s of the
column and all parent columns. Used by
when a cell references more than one header.
<td>`datatable-base`</td>
<h2 id="formatter-props">Appendix B: Formatter Argument Properties</h2>
The properties below are found on the object passed to `formatter`
functions defined in a column configuration. See
<a href="#nodeformatter-props">Appendix C</a> for the object properties
passed to `nodeFormatter`s.
<div id="formatter-props-table" class="yui3-skin-sam">
<th scope="col">Property</th>
<th scope="col">Description</th>
formatter: function (o) {
// assumes a numeric value for this column
The raw value from the record Model to populate this cell.
formatter: function (o) {
The Model data for this row in simple object format.
formatter: function (o) {
formatter: function (o) {
// Use a custom column property
The column configuration object.
formatter: function (o) {
A string of class names to add `<td class="HERE">` in
addition to the column class and any classes in the
column's `className` configuration.
formatter: function (o) {
The index of the current Model in the ModelList.
<em>Typically</em> correlates to the row index as well.
formatter: function (o) {
A string of css classes to add `<tr class="HERE"><td...`.
This is useful to avoid the need for `nodeFormatter`s to add
classes to the containing row.
<h2 id="nodeformatter-props">Appendix C: nodeFormatter Argument Properties</h2>
The properties below are found on the object passed to `nodeFormatter`
functions defined in a column configuration. See
<a href="#formatter-props">Appendix B</a> for the object properties
// Reference nodeFormatter
nodeFormatter: function (o) {
'<button class="term">terminate</button>' +
<div id="nodeformatter-props-table" class="yui3-skin-sam">
<th scope="col">Property</th>
<th scope="col">Description</th>
<td>The `<td>` Node for this cell.</td>
If the cell `<td>` contains an element with class
"yui3-datatable-liner", this will refer to that Node.
Otherwise, it is equivalent to `
o.td` (default behavior).
By default, liner elements aren't rendered into cells, but
to implement absolute column widths, some cell liner
element with `width` and `overflow` style is required
(barring a table style of `table-layout: fixed`). This may
be applied to the columns `cellTemplate` configuration or
to the `bodyView` instance's `CELL_TEMPLATE` for all
Generally, the liner, if present, corresponds to where the
content should go, so use `
o.cell` to add content and
`
o.td` to specifically work with the `<td>` Node.
The raw value from the record Model to populate this cell.
<td>The Model data for this row in simple object format.</td>
<td>The Model for this row.</td>
<td>The column configuration object.</td>
The index of the current Model in the ModelList.
<em>Typically</em> correlates to the row index as well.
YUI.add('datatable-node', function (Y) {
initializer: function (config) {
var record = (i % count) ?
columns[i] = { key: columns[i], allowHTML: true }
}, '0.1', { requires: ['datatable-base', 'node-pluginhost'] });
YUI({ filter: 'raw' }).use('datatable-node', 'datatable-sort', function (Y) {
Y.one('#column-config-table table').set('datatable', {
sortable: ['Configuration', 'Module']
Y.one('#formatter-props-table table').set('datatable', true);
Y.one('#nodeformatter-props-table table').set('datatable', true);