Skip to content

Clarify "focusable" in the context of tabIndex IDL attribute  #4464

@muan

Description

@muan

Prior discussion for tabIndex IDL attribute can be found in #1786. But as far as I can see, that isn't an issue anymore (can be closed?). Specifically:

Chrome / Safari always return -1 for HTML elements that are not focusable (even after setting tabIndex to something else).

Major browsers all seem to reflect the values consistently now.

Current behavior of tabIndex

Test page: https://html-is.glitch.me/tabindex.html

UA/Results:

Chrome Safari Firefox
tabIndex values for chrome tabIndex values for Safari tabIndex values for Firefox

Spec

The tabIndex IDL attribute must reflect the value of the tabindex content attribute. Its default value is 0 for elements that are focusable and −1 for elements that are not focusable. https://html.spec.whatwg.org/#dom-tabindex

So my first question is: what determine "elements are focusable"?

@domenic kindly pointed out that there is specification for focus. However it is unclear if this would/should be the "focusable" definition for tabIndex?

In the focus specification, it mentions that "specifically focusable", "being rendered", and "actually disabled" are some of the characteristics considered when determining "focusable". As far as I can see, across the board, tabIndex do not consider them.

  • a without href is (theoretically) not "specifically focusable", but tabIndex is 0.
  • button[hidden] is (theoretically) not "being rendered", but tabIndex is 0.
  • button[disabled] is "actually disabled", but tabIndex is 0.

Motivation

Web developers often want to manage focus manually, most commonly in the case of the "tab trapping" behavior required for dialog patterns. To trap tab within a container, we will need to be able to tell what elements are indeed "focusable", but there is currently no easy way to do this. tabIndex is the closest thing we have, but not close enough.

For example, this is what we currently would need to do (non-exhaustive):

function focusable(el): boolean {
  return el.tabIndex >= 0 && 
    (el.tagName !== 'A' || el.hasAttribute('href')) && 
    (!el.type || el.type !== 'hidden') && 
    !el.disabled && !el.hidden && 
    !el.closest('[hidden]')
}

If tabIndex isn't suitable for this use case, it'd be great if we can get a isFocusable() check on elements. I'd be happy to open a new issue as needed once tabIndex's focus determination rule is clarified.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions