Reputation: 39615
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
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
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
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:
disabled
(IE actually gives you an error if you try), and file uploads have unusual behaviour for security reasonstabindex
There are likely to be other subtle exceptions and additions to this behaviour depending on browser.
Upvotes: 414
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
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
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:
:not([tabindex="-1"])
to :not([tabindex^="-"])
because it's perfectly plausible to generate -2
somehow. Better safe than sorry right?: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
!:not([disabled])
because disabled elements can never be focusable. So again it's useless to add it to every single element.Upvotes: 14
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