Tabla de datos (data table)
Ordenación y filtrado para una tabla que ya existe en el HTML. El servidor envía una <table class="iv-table"> normal; en el init, las cabeceras ordenables se convierten en botones con aria-sort, aparece el campo de filtro y un estado en vivo anuncia cuántas filas se ven. No se pagina ni se virtualiza nada: las filas que sirvas son las que se ordenan, así que mantén la tabla en un tamaño que una página pueda sostener.
<div class="iv-datatable" data-iv-component="datatable">
<div class="iv-datatable__filter">
<label class="iv-label" for="dt-orders-q">Filter rows</label>
<input class="iv-input" id="dt-orders-q" type="search" autocomplete="off" placeholder="Customer, city or status" data-iv-datatable-filter>
</div>
<div class="iv-table-wrap" role="region" aria-labelledby="dt-orders-caption" tabindex="0">
<table class="iv-table">
<caption id="dt-orders-caption">Sample orders. The names, cities and totals below are made up for this demo; nothing here comes from a real account.</caption>
<thead>
<tr>
<th scope="col" data-iv-sort>Customer</th>
<th scope="col" data-iv-sort="text">City</th>
<th scope="col" data-iv-sort="date">Placed</th>
<th scope="col" class="iv-u-text-end" data-iv-sort="number">Total (EUR)</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Ada Márquez</th>
<td>Córdoba</td>
<td><time datetime="2026-03-04">4 Mar 2026</time></td>
<td class="iv-u-text-end">1,240.00</td>
<td><span class="iv-badge iv-badge--success">Shipped</span></td>
</tr>
<tr>
<th scope="row">Bruno Oliveira</th>
<td>Porto</td>
<td><time datetime="2026-01-22">22 Jan 2026</time></td>
<td class="iv-u-text-end">318.50</td>
<td><span class="iv-badge">Draft</span></td>
</tr>
<tr>
<th scope="row">Chiara Rossi</th>
<td>Milano</td>
<td><time datetime="2026-05-11">11 May 2026</time></td>
<td class="iv-u-text-end">2,905.75</td>
<td><span class="iv-badge iv-badge--success">Shipped</span></td>
</tr>
<tr>
<th scope="row">Diego Fernández</th>
<td>Alicante</td>
<td><time datetime="2026-02-08">8 Feb 2026</time></td>
<td class="iv-u-text-end">76.20</td>
<td><span class="iv-badge iv-badge--warning">On hold</span></td>
</tr>
<tr>
<th scope="row">Elin Karlsson</th>
<td>Göteborg</td>
<td><time datetime="2026-04-27">27 Apr 2026</time></td>
<td class="iv-u-text-end">540.00</td>
<td><span class="iv-badge iv-badge--success">Shipped</span></td>
</tr>
<tr>
<th scope="row">Farid Haddad</th>
<td>Marseille</td>
<td><time datetime="2026-06-02">2 Jun 2026</time></td>
<td class="iv-u-text-end">1,088.90</td>
<td><span class="iv-badge iv-badge--danger">Cancelled</span></td>
</tr>
<tr>
<th scope="row">Greta Hoffmann</th>
<td>Leipzig</td>
<td><time datetime="2026-01-09">9 Jan 2026</time></td>
<td class="iv-u-text-end">9,410.00</td>
<td><span class="iv-badge iv-badge--success">Shipped</span></td>
</tr>
<tr>
<th scope="row">Hugo Álvarez</th>
<td>Ourense</td>
<td><time datetime="2026-03-30">30 Mar 2026</time></td>
<td class="iv-u-text-end">212.40</td>
<td><span class="iv-badge iv-badge--warning">On hold</span></td>
</tr>
<tr>
<th scope="row">Ingrid Dahl</th>
<td>Bergen</td>
<td><time datetime="2026-05-19">19 May 2026</time></td>
<td class="iv-u-text-end">47.00</td>
<td><span class="iv-badge">Draft</span></td>
</tr>
</tbody>
</table>
</div>
</div>// data-iv-component="datatable"
{
datatableFilter: true,
sort: true,
}Orden inicial y valores tipados
data-iv-sorted="asc" o "desc" en una cabecera ordena la tabla en cuanto se inicializa. Una celda puede llevar data-iv-value cuando el texto que se muestra no es el valor por el que hay que ordenar, y las celdas de fecha se leen de <time datetime>. Las celdas sin un valor válido van al final en ambas direcciones.
<div class="iv-datatable" data-iv-component="datatable" data-iv-status-text="Showing {visible} of {total} builds">
<div class="iv-table-wrap" role="region" aria-labelledby="dt-builds-caption" tabindex="0">
<table class="iv-table iv-table--striped">
<caption id="dt-builds-caption">Made-up build report, sorted by duration on load. The last row has no duration on purpose: rows without a value sort to the end in both directions.</caption>
<thead>
<tr>
<th scope="col" data-iv-sort>Job</th>
<th scope="col" class="iv-u-text-end" data-iv-sort="number" data-iv-sorted="desc">Duration</th>
<th scope="col" data-iv-sort="text">Result</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">bundle-css</th>
<td class="iv-u-text-end" data-iv-value="42.5">42.5 s</td>
<td>Passed</td>
</tr>
<tr>
<th scope="row">bundle-js</th>
<td class="iv-u-text-end" data-iv-value="18">18 s</td>
<td>Passed</td>
</tr>
<tr>
<th scope="row">unit-tests</th>
<td class="iv-u-text-end" data-iv-value="126.75">2 min 6.75 s</td>
<td>Passed</td>
</tr>
<tr>
<th scope="row">browser-tests</th>
<td class="iv-u-text-end" data-iv-value="394">6 min 34 s</td>
<td>Failed</td>
</tr>
<tr>
<th scope="row">size-report</th>
<td class="iv-u-text-end" data-iv-value="7.25">7.25 s</td>
<td>Passed</td>
</tr>
<tr>
<th scope="row">pack-smoke</th>
<td class="iv-u-text-end"></td>
<td>Skipped</td>
</tr>
</tbody>
</table>
</div>
</div>// data-iv-component="datatable"
{
statusText: "Showing {visible} of {total} builds",
sort: true,
sorted: "desc",
value: 42.5,
}Tipos de columna
| Atributo | Comparación | Valor de la celda |
|---|---|---|
data-iv-sort o data-iv-sort="text" | localeCompare, con orden numérico, insensible a acentos y mayúsculas | data-iv-value y, si no, el texto de la celda |
data-iv-sort="number" | numérica, tras quitar unidades y separadores | data-iv-value y, si no, el texto de la celda |
data-iv-sort="date" | cronológica (Date.parse) | data-iv-value; si no, <time datetime>; si no, el texto de la celda |
Opciones
| Opción | Atributo | Por defecto | Efecto |
|---|---|---|---|
filterDelay | data-iv-filter-delay | 150 | Milisegundos que se esperan tras escribir antes de filtrar las filas. |
emptyText | data-iv-empty-text | No rows match | Texto de la fila que aparece cuando el filtro lo oculta todo. |
statusText | data-iv-status-text | {visible} of {total} rows | Plantilla del estado en vivo; se sustituyen ambos marcadores. |
locale | data-iv-locale | idioma del documento | Configuración regional que usan las comparaciones de texto. |
Métodos y eventos
sort(columnIndexOrTh, direction?), filter(query), clearFilter(), reset() (orden original y sin filtro), destroy(); se leen sortColumn, sortDirection, rows (orden original), visibleRows y query. Eventos en el elemento raíz: iv:sort (cancelable, detail.column, direction, previousColumn, previousDirection) y después iv:sorted; iv:filter (cancelable, detail.query, previousQuery) y después iv:filtered con visible y total.
Teclado y tecnología asistiva
Los controles de ordenación son botones de verdad, así que Enter y Espacio los activan y su nombre accesible es el texto de la cabecera; la flecha es CSS decorativo. Solo la columna ordenada lleva aria-sort. El filtro es un campo de búsqueda nativo. Las filas ocultas usan el atributo hidden, de modo que el lector de pantalla no las lee, y el recuento de filas se anuncia mediante un elemento role="status". El foco no se mueve nunca al ordenar ni al filtrar.
Sin JavaScript
La tabla es una tabla normal en el orden en que la envió el servidor. El bloque de filtro no se muestra en absoluto, así que nadie ve un control que no hace nada. Sirve las filas en el orden más útil.
Errores frecuentes
- Miles de filas: este componente mantiene todas las filas en el DOM. Pagina en el servidor o filtra los datos antes de renderizar.
- Marcar una cabecera como ordenable cuando sus celdas tienen contenido mezclado (insignias, botones): pon el valor de ordenación en
data-iv-value. - Omitir el
<caption>y la región de desplazamiento etiquetada: la tabla necesita un nombre, con JavaScript o sin él.