Using tabindex for keyboard events
Up until recently, I had a fixed notion that had curiously evaded critical analysis, namely that you can only capture keyboard events — keydown, keyup and keypress — on either input elements or the document itself. I can't put my finger on the moment I acquired this (partially true but misleading) piece of practical knowledge, but I had taken it for granted ever since.
The old way
Here's the kind of thinking spanning from that premise: Let's say you're building a web app with lots of components that react to keyboard shortcuts. You'd need to attach all keydown handlers to the document
element and then figure out (and keep track of) which part of the app is "focused" so that you know what action you can take, especially when dealing with inputs vs. custom elements — hello document.activeElement
, unmaintainable code and nascent bug.
The better way
It starts with getting a better understanding of what makes a HTML element react to keyboard events. Peter-Paul Koch has an excellent overview over at Quirksmode. So it's actually one of these:
document
window
(if you exclude IE8 and earlier)- a
<form>
element - a focusable element
The last one is important, because now we can ask: what are focusable elements? Clearly, inputs and links are also focusable. But could I make any element focusable? Why yes, yes you can.
tabindex
is your new bicycle
You might remember tabindex
as the way to specify the order with which hyperlinks and form elements are traversed when the user presses the Tab key. But it's also how you can specify that arbitrary elements in your markup (the <span>
and <div>
elements that make up your components) are focusable and thus can capture keyboard events.
All we need to do is specify tabindex="0"
on our the outermost element of our components and we can start attaching keyboard events to it. Here's a quick demo:
Not only does this simplify our code, but we also get a nice accessibility boost. You can read the full MDN article for more details and guidelines for using the tabindex
property.
I, for one, have a web app to fix.