Skip to content

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:

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:

JS Bin View demo on JSBin

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.