Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
function setFocusIfFocusable(node) {
if (node.nodeType !== Node.ELEMENT_NODE) {
// Text and comment nodes aren't focusable.
return false;
}
if (node.disabled === true) {
// Disabled elements can't be focused.
return false;
}
if (node.tabIndex < 0) {
// The HTML spec says that negative tab index values indicate an element should be,
// "click focusable but not sequentially focusable".
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute
//
// The HTML focusable spec also says,
// "User agents should consider focusable areas with non-null tabindex values to be click focusable."
// https://html.spec.whatwg.org/multipage/interaction.html#focusable
//
// Despite this, it seems like some browsers (e.g. Chrome, Firefox) return -1 even for elements
// that don't accept focus, like HTMLImageElement or the outermost HTMLElement tag.
// I think this method should (at least for now) only concern itself with "sequentially focusable" elements.
// https://html.spec.whatwg.org/multipage/interaction.html#sequentially-focusable
return false;
}
if (node.offsetWidth === 0 || node.offsetHeight === 0) {
// Hidden items can't be focused.
return false;
}
// At this point we assume the element accepts focus, so let's try and see.
// Listen for a "focus" event to verify that focus was set.
// We could compare the node to document.activeElement after focus,
// but this would not handle the case where application code managed focus to automatically blur.
let didFocus = false;
const handleFocus = () => {
didFocus = true;
};
try {
node.addEventListener('focus', handleFocus);
node.focus();
} finally {
node.removeEventListener('focus', handleFocus);
}
return didFocus;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment