Responsive Table Techniques
How it works
Mark up each td
with a data attribute with the corresponding <th>
. So if <td>USA</td>
was our item and it appeared under the heading <th>Country</th>
I might add it like this -> <td data-heading=Country>USA</td>
.
Then what I do is usually display it like a beautiful normal table above 700 or 800 px, and for thin tablets, phone landscape, and phone portrait I'll do a reset of all table-related elements making them block. Once the table is reset for mobile, I now think about each <tr>
as a design unit. I often will use the first <td>
as a heading and then for other td
elements in the <tr>
I'll use the data-heading
as a before element, and make a 2-column chart with each td
as a row of two cells of data.
HTML
Here's some sample <table>
markup that will work with this:
<table>
<thead>
<tr>
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
</tr>
<tr>
<td>Cell 6</td>
<td>Cell 7</td>
<td>Cell 8</td>
</tr>
<tr>
<td>Cell 11</td>
<td>Cell 12</td>
<td>Cell 13</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Footer 1</th>
<th>Footer 2</th>
<th>Footer 3</th>
</tr>
</tfoot>
</table>
JavaScript
If you don't want to mark up the <td data-heading="">
values by hand, here's a JavaScript plugin I use to scan pages for a table and mark it up automatically:
var tables = document.getElementsByTagName('table');
for (i=0;i<tables.length;i++){
var headers = tables[i].getElementsByTagName('th'),
rows = tables[i].getElementsByTagName('tr'),
header = [];
if (tables[i].getAttribute('data-table') == undefined) {
tables[i].setAttribute('data-table',i)
}
if (tables[i].getAttribute('data-table-theme') == undefined) {
tables[i].setAttribute('data-table-theme','default')
}
for (h=0;h<headers.length;h++){
header.push(headers[h].innerHTML);
}
for (r=0;r<rows.length;r++){
var cells = rows[r].getElementsByTagName('td');
for (c=0;c<cells.length;c++){
if (cells[c].getAttribute('data-header') == undefined && header[c] !== undefined) {
cells[c].setAttribute('data-header',header[c]);
}
}
}
}
Styles
Here's the CSS, including a mobile reset for table elements, turning them to block, and a reset back to normal size
CSS
@media (max-width: 400px) {
table,
table caption,
table thead,
table tbody,
table tfoot,
table tr,
table th,
table td { display: block; width: 100%; }
}
@media (min-width: 400px) {
table { display: table; width: auto; }
table caption { display: table-caption; width: auto; }
table thead { display: table-header-group; width: auto; }
table tbody { display: table-row-group; width: auto; }
table tfoot { display: table-footer-group; width: auto; }
table tr { display: table-row; width: auto; }
table th,
table td { display: table-cell; width: auto; }
}
Element Queries
Here's the same code using element queries, powered by the EQCSS plugin:
@element 'table' and (max-width: 400px) {
$this,
$this caption,
$this thead,
$this tbody,
$this tfoot,
$this tr,
$this th,
$this td { display: block; width: 100%; }
}
@element 'table' and (min-width: 400px) {
$this { display: table; width: auto; }
$this caption { display: table-caption; width: auto; }
$this thead { display: table-header-group; width: auto; }
$this tbody { display: table-row-group; width: auto; }
$this tfoot { display: table-footer-group; width: auto; }
$this tr { display: table-row; width: auto; }
$this th,
$this td { display: table-cell; width: auto; }
}
The advantage of element queries being that these breakpoints would be tied to the width of the <table>
element itself, not the width of the browser. This one set of breakpoints could handle the responsive support for every table on a website, regardless of which layouts it appears in. The same styles (thanks to style scoping and the $this
meta-selector) can also power multiple <table>
elements at different widths on the same page at the same time without changing the styles of all of them.