ZL
About Articles Contact
Published on Jan 29, 2020
Filed under:
#javascript

Getting keyboard-focusable elements

If you create JavaScript widgets, one of the key parts to accessibility is managing focus.

To manage focus, you need to find keyboard-focusable elements.

When you know the contents

It’s easy to find keyboard-focusable elements if you know the contents of the element beforehand.

For example, I know the focusable elements in this modal are <input> and <button>.

Modal with two inputs and one button.

I can get the focusable elements with querySelectorAll.

const focusableElements = [...modal.querySelectorAll('input, button')]

When you don’t know the contents

It’s harder to find keyboard-focusable elements if you don’t know the content beforehand.

After some research, I realised you could only focus on these elements with a keyboard:

  1. <a> with the href attribute
  2. <button>
  3. <input>
  4. <textarea>
  5. <select>
  6. <details>
  7. Elements with tabindex set to 0
  8. Elements with tabindex set to a positive number

We can get all keyboard-focusable elements with the following querySelectorAll. It looks a little complicated, but there’s no other way to include everything:

const keyboardfocusableElements = document.querySelectorAll(
'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])',
)

Some elements (like button) can be disabled. Disabled elements are not focusable.

In some libraries form fields are hidden visually (CSS) and for At (aria-hidden="true") and replaced by better looking components that should be accessible.

We can remove these elements with filter.

const keyboardfocusableElements = [
...document.querySelectorAll(
'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])',
),
].filter(el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'))

Turning it into a function

This querySelectorAll code is hard to read. We can put the entire thing into a function to make it more understandable.

/**
* Gets keyboard-focusable elements within a specified element
* @param {HTMLElement} [element=document] element
* @returns {Array}
*/
function getKeyboardFocusableElements(element = document) {
return [
...element.querySelectorAll(
'a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])',
),
].filter(
el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'),
)
}

Update: Lauren contacted me and asked about display:none. Certainly these elements won’t be focusable so we have to filter them out too. I also included a list of other elements that I never thought about before — embed, object, iframe, etc.

I updated the code in my Github repository — you can get the updated code if you sign up below.

Previous First look at Snowpack Next How I work with arrays

Join My Newsletter

I share what I’m learning on this newsletter: code, building businesses, and living well.

Sometimes I write about technical deep-dives, product updates, musings on how I live, and sometimes my struggles and how I’m breaking through.

Regardless of the type of content, I do my best to send you at least one insightful piece every week.

If you’re into making things and growing as a person, you’ll probably feel at home here.

“

Zell is the rare sort of developer who both knows his stuff and can explain even the most technical jargon in approachable — and even fun — ways!

I’ve taken his courses and always look forward to his writing because I know I’ll walk away with something that makes me a better front-ender.

Geoff Graham
Geoff Graham — Chief Editor @ CSS Tricks
The Footer

General

Home About Contact Testimonials Tools I Use

Projects

Magical Dev School Splendid Labz

Socials

Youtube Instagram Tiktok Github Bluesky X

Follow Along

Email RSS
© 2013 - 2026 Zell Liew / All rights reserved / Terms