Paul Turner
Paul Turner

Reputation: 39615

Which HTML elements can receive focus?

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.

Upvotes: 336

Views: 209017

Answers (7)

flavi1
flavi1

Reputation: 239

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>

Upvotes: 0

Bondsmith
Bondsmith

Reputation: 1578

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>

Upvotes: 1

bobince
bobince

Reputation: 536329

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.

Upvotes: 414

Yohannes Kristiawan
Yohannes Kristiawan

Reputation: 251

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

Upvotes: 7

ling
ling

Reputation: 10019

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.)

Upvotes: 12

whaley
whaley

Reputation: 645

$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.

Upvotes: 14

ReeCube
ReeCube

Reputation: 2587

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.

Upvotes: 55

Related Questions