320

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.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Paul Turner
  • 38,949
  • 15
  • 102
  • 166

6 Answers6

402

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.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
bobince
  • 528,062
  • 107
  • 651
  • 834
  • Any idea how to get all of these with jQuery? See also http://stackoverflow.com/questions/7668525/is-there-a-jquery-selector-to-get-all-elements-that-can-get-focus – ripper234 Oct 05 '11 at 22:29
  • 3
    I found some interesting results: http://jsfiddle.net/B7gn6/ suggests to me that the "tabindex" attrib is not enough to work in Chrome at least.. – jrz Jan 07 '13 at 00:59
  • 1
    @jon: those tabindex elements do actually accept focus, though, if you try clicking them. Testing `.is(':focus')` is not reliable - jQuery implements its own test for `:focus` on Chrome, which is what you're actually testing here. That jQuery code is unreliable/buggy (`~tabIndex`..?! That ain't gonna work...) – bobince Jan 07 '13 at 12:44
  • @bobince Really interesting. +1 for the last point i.e. usage of `tabIndex` for elements that doesn't support `focus` by default. Really very useful. Wanted to give you +10 if I could. – Rajesh Paul Nov 04 '13 at 06:26
  • 32
    That the tabindex attribute "allows authors to control whether an element is supposed to be focusable" is standardized in HTML5: http://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute Basically, a value of 0 makes the element focusable but leaves its ordering up to the browser. – natevw Mar 31 '14 at 22:58
  • Very helpful thank you. If you only need to focus temporarily and don't want to retain the tabindex, you can do something like this in jQuery: $('#el').attr('tabindex', 1).focus().blur(function() {$(this).attr('tabindex', null)}); – Synexis Nov 30 '14 at 09:19
  • 12
    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.). – mems Jan 09 '15 at 19:40
  • `$("#filenameInput").click();` kills IE submit() – gordon Mar 13 '15 at 16:31
  • 22
    An element with a tabindex of -1 may receive focus programmatically through the focus method; it just can't be tabbed to. – jessebeach Sep 06 '15 at 23:57
  • like @mems says, IE allows focusing other elements under certain conditions. We had a weird bug at our company because a container DIV was getting a `focus` before the child ever got a `click`. The answer is good but should be edited to contain this. – btx9000 Oct 21 '15 at 10:57
  • Thanks, @natevw, for the link. Our fellow developers may also find this doc useful, which includes a discussion of the way `tabIndex` has been standardised in HTML5 (according to Mozilla, anyhow): https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets – Michael Scheper Jan 13 '16 at 00:19
  • 9
    …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: http://jsfiddle.net/0jz0kd1a/ , first try to click the element, then change tabindex to 0 and try to use tab. – daremkd Jan 28 '16 at 12:23
  • `tabindex` doesn't seem to let `form` receive focus, at least in Firefox. – Volker E. Mar 13 '16 at 06:11
  • Important point jessebeach makes. The answer is misleading at the end when it says: "…unless the tabindex is -1, which makes focus impossible." This isn't true. You can use `.focus()` to focus elements with `tabindex="-1"` it just means they won't be in the tabbing order that's all and that's true for all modern browsers i've tested on. Please update? – David Brewer Mar 01 '17 at 17:15
  • Unfortunately, a quick glance at MDN shows tab-index focusing is better supported by some browsers than others. – Jay Edwards Oct 10 '18 at 18:24
  • For anyone still coming to this answer, I found that I had to add a `tabindex` in Chrome for my `

    ` to receive focus even when it had `contenteditable=true`. Without that, Chrome never focused the paragraph and always returned the `

    ` as the `activeElement`.
    – ToastyMallows Nov 30 '20 at 16:41
  • The `` element is missing from the list of focusable elements. – Waruyama Apr 21 '22 at 07:09
53

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.

Community
  • 1
  • 1
ReeCube
  • 2,545
  • 17
  • 23
  • @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 `Bar` is certainly focusable because it's an `a` element that has an `href` attribute. But your selector does not include it. – jbyrd Aug 31 '15 at 19:43
  • @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. – TWiStErRob Aug 31 '15 at 22:11
  • SASS (or CSS) is a suitable form to provide a rigorous answer to the above question (barring browser inconsistencies). – Roy Tinker Dec 03 '15 at 23:44
  • `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: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex – Lazzaro Mar 13 '16 at 19:26
  • I wouldnt exclude items with tabindex-1. Users won't be able to focus via any input devices, but you may want to programmatically set and show that focus still. – James Westgate Apr 19 '16 at 08:16
  • 2
    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 – Bashu Naimi-Roy Oct 07 '21 at 17:10
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.
whaley
  • 598
  • 4
  • 12
  • 2
    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. – Jeff Luyet Aug 12 '21 at 06:50
  • 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 `nope` 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 tag is encountered it satisfies the selector – Bashu Naimi-Roy Oct 07 '21 at 17:23
11

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

2540625
  • 11,022
  • 8
  • 52
  • 58
ling
  • 9,545
  • 4
  • 52
  • 49
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

2

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>
Bondsmith
  • 1,343
  • 3
  • 13
  • 27
  • Do you mind if there is something like "onfocus" function too? - any function that do a trick by UI will make disappointment soon. – MMMahdy-PAPION Sep 20 '21 at 02:54
  • Are you asking for a focus event listener? like this -> https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event – Bondsmith Sep 21 '21 at 08:40
  • 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. – MMMahdy-PAPION Sep 22 '21 at 04:02
  • 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. – Bondsmith Sep 22 '21 at 14:40
  • Better but still an issue, if the element have a **passive** EventListener like the one that we add temporary, it will run before the stopImmediatePropagation() work - also look like Safari not support preventScroll yet – MMMahdy-PAPION Sep 23 '21 at 02:27
  • Safari supports it just fine. However, it has a proposed bug in some particular cases that will not occur in this case. – Bondsmith Sep 23 '21 at 03:05
  • As for the passive event listener, you can take care of it the same way, create an event protector function for passive listeners. I leave that to you, dear. Just add that yourself, and any other requirement you might have in case you wanted to use this. – Bondsmith Sep 23 '21 at 03:15
  • Sadly, no way to the handle the passive (your function protector is already passive) and still i think these kind of ways are not safe. Thanks, my goal was to clarify the issue, not helping myself. Your nice effort are admirable, i think this answer should get up-vote more. – MMMahdy-PAPION Sep 23 '21 at 03:59
  • I must admit to a degree that it ain't peachy using UI stuff, thanks a bunch anyways. – Bondsmith Sep 23 '21 at 14:53
  • 1
    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? – Arad Alvand Oct 25 '21 at 14:10