in src/utils/iterate-focusable-elements.ts [76:109]
export function isFocusable(elem: HTMLElement, strict = false): boolean {
// Certain conditions cause an element to never be focusable, even if they have tabindex="0"
const disabledAttrInert =
['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET'].includes(elem.tagName) &&
(elem as HTMLElement & {disabled: boolean}).disabled
const hiddenInert = elem.hidden
const hiddenInputInert = elem instanceof HTMLInputElement && elem.type === 'hidden'
if (disabledAttrInert || hiddenInert || hiddenInputInert) {
return false
}
// Each of the conditions checked below require a reflow, thus are gated by the `strict`
// argument. If any are true, the element is not focusable, even if tabindex is set.
if (strict) {
const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0
const visibilityInert = ['hidden', 'collapse'].includes(getComputedStyle(elem).visibility)
const clientRectsInert = elem.getClientRects().length === 0
if (sizeInert || visibilityInert || clientRectsInert) {
return false
}
}
// Any element with `tabindex` explicitly set can be focusable, even if it's set to "-1"
if (elem.getAttribute('tabindex') != null) {
return true
}
// One last way `elem.tabIndex` can be wrong.
if (elem instanceof HTMLAnchorElement && elem.getAttribute('href') == null) {
return false
}
return elem.tabIndex !== -1
}