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 Free Workshop on Branding and Marketing yourself as a developer Next Emulating Mac's Dvorak-Qwerty-⌘ on Windows

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 to live, and sometimes my struggles and how I’m breaking through.

Regardless of the type of content, I do my best to send you an update every week.

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

“

Zell is one of those rare people who commands tremendous knowledge and experience but remains humble and helpful. They want you to know what they know, not just be impressed by it.

In other words, Zell is a natural teacher. You’re lucky to have him because he feels lucky to be able to help you in your journey.

Heydon Pickering
Heydon Pickering — Web & Accessibility Extraordinaire
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 - 2025 Zell Liew / All rights reserved / Terms