I'm Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with HTML, CSS and SVG. To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

my books

Pro CSS3 Animation book coverPro CSS3 Animation, Apress, 2013

my other blogs

Massive Head CanonMassive Head Canon: Intelligent discussion of movies, books, games, and technology.

my projects

The New DefaultsThe New Defaults — A Sass color keyword system for designers.

CSSslidyCSSslidy — an auto-generated #RWD image slider. 3.8K of JS, no JQuery.

A Fresh Approach to Responsive HTML5 Tables

A new elegant way to create fluid, responsive tables

Recently Geoff Yuen demonstrated a very effective technique for creating responsive HTML tables. I had just two small issues with the demo: the CSS was written in Sass, and there was a great deal of replication and redundancy in the use of data attributes. The solution shown here addresses both issues. You’ll need to view this page in a browser window less than 600 pixels wide to witness the transition into the alternative layout of the table.

The Base Code

I’ll start with a close interpretation of Geoff’s code:

<table id="miyazaki">
<caption>The Films of Miyazaki</caption>
<thead>
<tr><th>Film<th>Year<th>Honor
<tbody>
<tr>
<td data-th="Film">My Neighbor Totoro
<td data-th="Year">1988
<td data-th="Honor">Blue Ribbon Award (Special)
<tr>
<td data-th="Film">Princess Mononoke
<td data-th="Year">1997
<td data-th="Honor">Nebula Award (Best Script)
<tr>
<td data-th="Film">Spirited Away
<td data-th="Year">2001
<td data-th="Honor">Academy Award (Best Animated Feature)
<tr>
<td data-th="Film">Howl’s Moving Castle
<td data-th="Year">2004
<td data-th="Honor">Hollywood Film Festival (Animation OTY)
</table>

Note the repetition of the data attribute, and the reflection of the appropriate header content in each table cell. The base CSS:

table#miyazaki caption {
font-size: 2rem; color: #444;
margin: 1rem;
background-image: url(miyazaki.png), url(miyazaki2.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center left, center right;
}
table#miyazaki {
border-collapse: collapse;
font-family: Agenda-Light; font-weight: 100;
background: #333; color: #fff;
text-rendering: optimizeLegibility;
border-radius: 5px;
}
table#miyazaki thead th { font-weight: 600; }
table#miyazaki thead th, table#miyazaki tbody td {
padding: .8rem; font-size: 1.4rem; 
}
table#miyazaki tbody td {
padding: .8rem; font-size: 1.4rem;
color: #444; background: #eee;
}
table#miyazaki tbody tr:not(:last-child) {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd; 
}

The responsive CSS for converting the table:

@media screen and (max-width: 600px) {
table#miyazaki caption { background-image: none; }
table#miyazaki thead { display: none; }
table#miyazaki tbody td { display: block; padding: .6rem; }
table#miyazaki tbody tr td:first-child { background: #333; color: #fff; }
table#miyazaki tbody td:before {
content: attr(data-th); font-weight: bold;
display: inline-block; width: 6rem; 
}
}

The media query hides the header cells of the table and displays the hidden data-th value as a label in front the contents of each table cell, on its own line. Special treatment is given to the first cell for each transformed row, to keep the data distinct.

Automating The Result

This works very well, as you can see if you resize the browser window. However, it’s not terribly scalable: adding a new row means manually inserting data attributes for each table cell. While that work could be done with a server-side language such as PHP, the same could be achieved with JavaScript, if you were willing to sacrifice some progressive enhancement.

First, we simplify the table:

<table id="miyazaki">
<caption>The Films of Hayao Miyazaki</caption>
<thead>
<tr><th>Film<th>Year<th>Honor
<tbody>
<tr>
<td>My Neighbor Totoro
<td>1988
<td>Blue Ribbon Award (Special)
<tr>
<td>Princess Mononoke
<td>1997
<td>Nebula Award (Best Script)
<tr>
<td>Spirited Away
<td>2001
<td>Academy Award (Best Animated Feature)
<tr>
<td>Howl’s Moving Castle
<td>2004
<td>Hollywood Film Festival (Animation OTY)
</table>

At the end of the document, add a small script:

<script>
var headertext = [];
var headers = document.querySelectorAll("#miyazaki th"),
tablerows = document.querySelectorAll("#miyazaki th"),
tablebody = document.querySelector("#miyazaki tbody");
for(var i = 0; i < headers.length; i++) {
var current = headers[i];
headertext.push( current.textContent.replace( /\r?\n|\r/,"") );
}
for (var i = 0, row; row = tablebody.rows[i]; i++) {
for (var j = 0, col; col = row.cells[j]; j++) {
col.setAttribute("data-th", headertext[j]);
} }
</script>

In English, this script gains the text content of each <th> cell, stripping it of return and newline characters. That text is then applied as a data attribute value to the appropriate table cells, yielding the same result if the CSS is included. (setAttribute is used rather than dataset, as the latter is only just supported in IE 11.)

The result is still accessible – the inserted DOM text is readable in VoiceOver, for example – although it will require more testing. Experiment with the code for this table on CodePen

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.