Making elements keyboard focusable and clickable

When you want to make an element on a web page clickable in order to trigger a JavaScript function, the best option is to use an element that has native support for keyboard interaction. In most cases a button (either <input type="button" /> or <button type="button"></button>) is the most appropriate element. It lets everybody trigger your function, whether they use a mouse or not.

Unfortunately, many (maybe even most) developers do not use the correct HTML elements for this. Instead they often use elements that have no native support for keyboard interaction, like span, div or li. A slightly better choice is the a element since it can receive keyboard focus as long as it has a non-empty href attribute (i.e. <a href="#">Click me</a>). But it isn’t really a link anymore since it acts like a button and doesn’t lead anywhere.

So why do so many developers choose the wrong element to trigger functionality? I think the main reason is styling. Once upon a time browsers did not let you change the way button elements look a whole lot, which forced developers to use elements that could be styled freely. It is many years and browser versions ago that this was necessary though (see some examples of Styled submit buttons from 2004). All browsers in widespread use today let you change the way buttons look.

Another reason is lack of knowledge. Many don’t know that you can style buttons these days, and many others don’t know that using elements without native keyboard support can easily cause serious accessibility problems.

What do I suggest then? There are two options:

  1. Use real buttons and style them so they look the way you need them to look.
  2. If you simply cannot or will not use real buttons, make sure your fake buttons can be focused and activated without using a mouse.

In both cases, to avoid confusing non-JS users, you should also use JavaScript to add interactive elements to the DOM unless they do something useful even if JavaScript is disabled.

Option 1 is easiest and most robust, so that’s what I recommend. But if you choose option 2, here’s what you need to do for your fake button to mimic a real button as best as it can:

  • Assign a click event handler (well, you need to do that with option 1 too obviously)
  • Give the element a tabindex="0" attribute to make it focusable and appear in the tab order according to its location in the DOM
  • Use a role attribute to tell assistive technology what the element mimics (in the case of a button, role="button")
  • Attach a keyboard event handler (keydown) that triggers a click event when the element has focus and the Return key or the Space key is pressed
  • If you’re using a link to create the fake button, disable its default click behaviour (via event.preventDefault() or return false)

If you use jQuery, inserting a span element that works like a button might look like this:

$('<span>', {
    'class': 'button', // So you can style it
    role: 'button', // Tell assistive technology it's a button
    tabindex: '0', // Make it keyboard focusable
    click: function() { // Make something happen when it is clicked
        alert('You clicked me!');
    },
    keydown: function(e) { // Trigger the click event from the keyboard
        var code = e.which;
        // 13 = Return, 32 = Space
        if ((code === 13) || (code === 32)) {
            $(this).click();
        }
    },
    html: 'Click me!'
}).appendTo($('#button-container'));

See the Making elements keyboard focusable and clickable demo page to compare the following four different approaches:

  • Button 1 is a span element with a click handler
  • Button 2 is a span element with a click handler plus tabindex and role attributes
  • Button 3 is a span element with a click handler, tabindex and role attributes, and a keydown handler
  • Button 4 is a button element with a click handler

For mouse users all of these work. Click any of the button-looking elements and an alert will pop up. But if you use the keyboard to tab to each of the buttons, here’s what happens:

  • Button 1: No keyboard focus in any browser.
  • Button 2: Gets keyboard focus in all browsers, but only Opera triggers the click event, and only when when you press Return, not Space. Screen readers that support WAI-ARIA announce the element as a button and make it clickable (verified with VoiceOver + Safari on OS X 10.8 and NVDA + IE9 on Windows 7) thanks to the role="button" attribute.
  • Button 3: Gets keyboard focus and triggers the click event in all browsers when the Return key or the Space key is pressed. Same screen reader results as Button 2.
  • Button 4: Gets keyboard focus and triggers the click event in all browsers when the Return key or the Space key is pressed. Same screen reader results as Button 2.

The bottom line is this: either use natively keyboard accessible elements (preferable) or make what you use instead mimic the appropriate native element as closely as possible.

Posted on February 12, 2013 in Accessibility, JavaScript

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.