Guess what time is it kids? It’s time for yet another boring, technical post. It’s that sort of week. Or rather it was – I usually queue my posts approximately 8-10 days in advance. This means that if I get hit by a bus one day, you won’t know anything is wrong until a week or so later. But I digress…
One of the web apps I maintain has a big table that looks like this:
<table> <tr> <td><input class='flat' name='foo'></td> <td><input class='flat' name='bar'></td> <td><input class='flat' name='baz'></td> <td><input class='flat' name='bin'></td> <td><input class='flat' name='bom'></td> <td><input class='flat' name='bam'></td> </tr> . . . . . . . . . <tr> <td><input class='flat' name='foo'></td> <td><input class='flat' name='bar'></td> <td><input class='flat' name='baz'></td> <td><input class='flat' name='bin'></td> <td><input class='flat' name='bom'></td> <td><input class='flat' name='bam'></td> </tr> </table>
Basically, every cell contains an input box. Each of them has an onChange trigger which will submit it’s contents to the database via AJAX call. It basically allows the user to pull up a full page of records and edit them en masse without doing a lot of clicking.
Unfortunately this setup is a pain in the ass to navigate. Tabbing over works fine but only in one direction. The most intuitive way to traverse such a structure would be with the arrow keys – you know, just like a big spreadsheet. Sadly it just doesn’t work that way. But we can make it work like that with just a pinch of jQuery magic:
$(document).ready(function() { $("input.flat").keypress( function (e) { switch(e.keyCode) { // left arrow case 37: $(this).parent() .prev() .children("input.flat") .focus(); break; // right arrow case 39: $(this).parent() .next() .children("input.flat") .focus(); break; // up arrow case 40: $(this).parent() .parent() .next() .children("td") .children("input.flat[name=" +$(this).attr("name")+"]") .focus(); break; // down arrow case 38: $(this).parent() .parent() .prev() .children("td") .children("input.flat[name=" +$(this).attr("name")+"]") .focus(); break; } }); });
How does this work? Each time you press a key while one of the input boxes is in focus, I check the key code. If the code corresponds to one of the arrow key values I switch focus. The statements up there look a bit convoluted so let me explain one in more detail. Let’s do the left arrow:
- First we use the parent() function to get the parent node of the input box. This gives us the <td> tag
- Second, we use the prev() to get the previous sibling of our <td> node
- Third, we get the list of children of that node – and we narrow it down to just input boxes with the specific class. Because of the way our table is structured, we know there will always be exactly one child there
- Finally we call the focus() function on all the children (remember, there is only one there) which moves the cursor over
Moving up and down, requires extra steps. We call parent() twice to back out to the <tr> tag and grab previous or next sibling of that. Then we grab all the <td> children, and their children but narrowing it down to input boxes with the same name as the one that triggered the focus change. Once again there will always be exactly one node there.
End result is intuitive keyboard navigation that allows you to move in the table very much the way you move around in an Excel spreadsheet. I thought this was a pretty neat effect so I decided to share it here.
I created a live demo to demonstrate this effect. Mess around with it and let me know what you think. It’s a simple little tweak, but makes a huge usability difference – at least in my opinion.
Does anyone has an idea how to make this prettier, faster, better, stronger and etc? This code is probably suboptimal, but it works. I actually tested it on large-ish data sets, and it didn’t seem to cause visible slowdowns. But of course I’m always open for constructive criticism.

/dev/random