344

I'm looking for a definitive list of HTML elements which are allowed to take focus, i.e. which elements will be put into focus when focus() is called on them?

I'm writing a jQuery extension which works on elements that can be brought into focus. I hope the answer to this question will allow me to be specific about the elements I target.

1
  • 1
    The article Focus on web.dev is great. Commented Apr 21, 2024 at 2:00

7 Answers 7

418

There isn't a definite list, it's up to the browser. The only standard we have is DOM Level 2 HTML, according to which the only elements that have a focus() method are HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement and HTMLAnchorElement. This notably omits HTMLButtonElement and HTMLAreaElement.

Today's browsers define focus() on HTMLElement, but an element won't actually take focus unless it's one of:

  • HTMLAnchorElement/HTMLAreaElement with an href
  • HTMLInputElement/HTMLSelectElement/HTMLTextAreaElement/HTMLButtonElement but not with disabled (IE actually gives you an error if you try), and file uploads have unusual behaviour for security reasons
  • HTMLIFrameElement (though focusing it doesn't do anything useful). Other embedding elements also, maybe, I haven't tested them all.
  • Any element with a tabindex

There are likely to be other subtle exceptions and additions to this behaviour depending on browser.

Sign up to request clarification or add additional context in comments.

17 Comments

I found some interesting results: jsfiddle.net/B7gn6 suggests to me that the "tabindex" attrib is not enough to work in Chrome at least..
That the tabindex attribute "allows authors to control whether an element is supposed to be focusable" is standardized in HTML5: w3.org/TR/html5/… Basically, a value of 0 makes the element focusable but leaves its ordering up to the browser.
All elements with element.isContentEditable === true are focusable too. Note that IE -10 (11+?) can focus any element with display block or table (div, span, etc.).
An element with a tabindex of -1 may receive focus programmatically through the focus method; it just can't be tabbed to.
…unless the tabindex is -1, which makes focus impossible >> not true, if tabindex is -1, focusing by CLICKING is possible, but focusing by pressing "tab" is impossible. -1 makes an element focusable, just it isn't added in the tabbing order. See: jsfiddle.net/0jz0kd1a , first try to click the element, then change tabindex to 0 and try to use tab.
|
56

Here I have a CSS-selector based on bobince's answer to select any focusable HTML element:

  a[href]:not([tabindex='-1']),
  area[href]:not([tabindex='-1']),
  input:not([disabled]):not([tabindex='-1']),
  select:not([disabled]):not([tabindex='-1']),
  textarea:not([disabled]):not([tabindex='-1']),
  button:not([disabled]):not([tabindex='-1']),
  iframe:not([tabindex='-1']),
  [tabindex]:not([tabindex='-1']),
  [contentEditable=true]:not([tabindex='-1'])
  {
      /* your CSS for focusable elements goes here */
  }

or a little more beautiful in SASS:

a[href],
area[href],
input:not([disabled]),
select:not([disabled]),
textarea:not([disabled]),
button:not([disabled]),
iframe,
[tabindex],
[contentEditable=true]
{
    &:not([tabindex='-1'])
    {
        /* your SCSS for focusable elements goes here */
    }
}

I've added it as an answer, because that was, what I was looking for, when Google redirected me to this Stackoverflow question.

EDIT: There is one more selector, which is focusable:

[contentEditable=true]

However, this is used very rarely.

6 Comments

@TWiStErRob - your selector doesn't target the same elements as @ReeCube's selectors, because yours does not include elements that don't have a tabindex explicitly set. For example <a href="foo.html">Bar</a> is certainly focusable because it's an a element that has an href attribute. But your selector does not include it.
@jbyrd that was just a call for edit based on bobince's statement: "…unless the tabindex is -1, which makes focus impossible.", it was never supposed to replace ReeCube's answer; see the edit history.
SASS (or CSS) is a suitable form to provide a rigorous answer to the above question (barring browser inconsistencies).
tabindex="-1" does not make an element unfocusable, it just can't be focused by tabbing. It still may receive focus by clicking on it or programmatically with HTMLElement.focus(); same for any other negative number. See: developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…
I would recommend using input:not([type="hidden"]), unless you want the selector to focus an element that is visually and screen-reader hidden by browser default
|
14
$focusable:
  'a[href]',
  'area[href]',
  'button',
  'details',
  'input',
  'iframe',
  'select',
  'textarea',

  // these are actually case sensitive but i'm not listing out all the possible variants
  '[contentEditable=""]',
  '[contentEditable="true"]',
  '[contentEditable="TRUE"]',

  '[tabindex]:not([tabindex^="-"])',
  ':not([disabled])';

I'm creating a SCSS list of all focusable elements and I thought this might help someone due to this question's Google rank.

A few things to note:

  • I changed :not([tabindex="-1"]) to :not([tabindex^="-"]) because it's perfectly plausible to generate -2 somehow. Better safe than sorry right?
  • Adding :not([tabindex^="-"]) to all the other focusable selectors is completely pointless. When using [tabindex]:not([tabindex^="-"]) it already includes all elements that you'd be negating with :not!
  • I included :not([disabled]) because disabled elements can never be focusable. So again it's useless to add it to every single element.

2 Comments

The part about not having to add :not([disabled]) on every line is wrong. Don't believe me? Do a document.querySelectorAll(':not([disabled])') and see what happens. Not disabled means you want all the enabled elements.
Similarly the part about not having to add :not([tabindex^="-"]. Proof: Try this line in your console: document.querySelectorAll('a, [tabindex]:not([tabindex="-1"]')[0] It will select a node such as <a href="shouldnt-be-focused" tabindex="-1">nope</a> For what it's worth I think this comes from the misunderstanding that these selectors are joined by "or" and not "and", so as soon as the <a> tag is encountered it satisfies the selector
12

The ally.js accessibility library provides an unofficial, test-based list here:

https://allyjs.io/data-tables/focusable.html

(NB: Their page doesn't say how often tests were performed.)

Comments

7

Maybe this one can help:

function focus(el){
	el.focus();
	return el==document.activeElement;
}

return value: true = success, false = failed

Reff: https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/activeElement https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus

1 Comment

It works, but it loses current focus state. :)
1

There is a much more elegant way to handle this:

Extend the element prototype like the sample below. Then you can use it like:

element.isFocusable()

*Returns true if "element" is focusable and false if not.

/**
* Determining if an element can be focused on
* @return   {Boolean}
*/
HTMLElement.prototype.isFocusable = function () {
  var current = document.activeElement
  if (current === this) return true
  var protectEvent = (e) => e.stopImmediatePropagation()
  this.addEventListener("focus", protectEvent, true)
  this.addEventListener("blur", protectEvent, true)
  this.focus({preventScroll:true})
  var result = document.activeElement === this
  this.blur()
  if (current) current.focus({preventScroll:true})
  this.removeEventListener("focus", protectEvent, true)
  this.removeEventListener("blur", protectEvent, true)
  return result
}

// A SIMPLE TEST
console.log(document.querySelector('a').isFocusable())
console.log(document.querySelector('a[href]').isFocusable())
<a>Not focusable</a>
<a href="#">Focusable</a>

11 Comments

Do you mind if there is something like "onfocus" function too? - any function that do a trick by UI will make disappointment soon.
Are you asking for a focus event listener? like this -> developer.mozilla.org/en-US/docs/Web/API/Element/focus_event
No dear, i am telling you that way of your function can make mistakes, for example if there be focus event added to the element, it will run while the developer going to check the element is focus-able or not.
That's not an issue dear, you can easily add an event protector function to focus and blur and remove it later on. I added it for you there, dear.
This is indeed an elegant solution. Good job! The only question I have is shouldn't we register event protectors for other focus-related events like focusin, focusout as well? And also shouldn't we add event protectors for current as well?
|
0

I use this

function isFocusable(el) {
    const cs = window.getComputedStyle(el, null);
    if (cs.getPropertyValue('visibility') == 'hidden' || cs.getPropertyValue('display') == 'none')
        return false;
    const natively = 'a[href], area[href], details, iframe, :is(button, input:not([type="hidden"]), select, textarea)';
    
    if (el.matches(natively) || (el.hasAttribute('tabindex') && parseInt(el.getAttribute('tabindex')) >= 0) || el.isContentEditable)
        return true;
    return false;
}
*:focus {
    outline: none !important;
    border: 4px solid red;
    box-shadow: 0 0 10px #FF5722;
}
<a id="e1" tabindex="0" role="button" aria-pressed="false">TOGGLE</a>

<span id="not-focusable">NOT FOCUSABLE</span>

<span id="e3" role="button">ACTION</span>

<button id="e4" aria-pressed="false">REAL BUTTON TOGGLE</button>

<button>REAL BUTTON ACTION</button>

<script>
const log = (ev) => {console.log('Focusable?', ev.target, isFocusable(ev.target) )}


document.getElementById('e1').addEventListener('click',  log)
document.getElementById('not-focusable').addEventListener('click',  log)
document.getElementById('e3').addEventListener('click',  log)
document.getElementById('e4').addEventListener('click',  log)

</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.