skip to content

Focus Management and tabindex

tabindex=0 puts an element in the tab order, -1 makes it focusable only in code, and positive values wreck the order for everyone. Interviewers pair this with dialogs and client-side routing, where focus must be moved deliberately or the user is stranded.

part ofHTMLoverview, primer and where to startread it →
on this pageshow

questions

5

In HTML, what do tabindex="0", tabindex="-1", and a positive value such as tabindex="3" each do to an element?

level: juniorimportance: must knowfreq 72%

answer

  1. three classes, not positions
  2. zero keeps DOM order
  3. negative means script-only focus
  4. positive jumps the whole queue
  5. focusable is not the same as interactive

basics

~20 s

tabindex="0" puts an element in the tab order at its DOM position; a negative value such as -1 makes it focusable by script or click but never by Tab; a positive value jumps it ahead of everything else and wrecks the order.

solid answer

~50 s

There are three classes of value, not an ordered list of positions. `tabindex="0"` makes an element sequentially focusable and leaves it exactly where it sits in DOM order, which is how you make a genuinely custom control Tab-reachable. A negative value — `-1` by convention, though any negative number behaves identically — keeps the element out of the Tab sequence but allows `element.focus()` and mouse focus; that is what you put on a dialog container, a heading, or a view target you intend to focus in code. A positive value opts the element out of DOM order entirely: every positive-tabindex element is visited first, ascending by value, before anything with `0` or no tabindex, so a single stray `tabindex="1"` reorders the whole page. That is why positive values are an anti-pattern. Native interactive elements are already in the tab order and need no tabindex at all.

code

html · 4 lines
html
<button>Native control, already focusable</button>
<div tabindex="0">Tab reaches this, in DOM order</div>
<div tabindex="-1" id="panel">Focusable only via panel.focus()</div>
<div tabindex="3">Tab visits this before all of the above</div>

go deeper

for a junior

Be able to state the three value classes plainly and say which elements are focusable without any tabindex. Knowing that positive values are avoided in practice is enough at this level.

for a middle

Explain the ordering rule the browser applies — positives first ascending, then everything else in DOM order — and give a concrete case where tabindex="-1" is the right tool rather than tabindex="0".

for a senior

Show the diagnosis: how you find a broken tab order in a real page, why a component library shipping positive values is a systemic defect, and how you would remove them without regressing keyboard access.

for a principal

Own the policy angle: a lint rule banning positive tabindex, a review convention that reaching for tabindex on a div is a design smell, and where focus management belongs in a shared component layer rather than in each feature.

## What "focusable" actually means The browser maintains a *sequential focus navigation order* — the list the Tab key walks. Some elements are on it by default: `<a>` with an `href`, `<button>`, `<input>`, `<select>`, `<textarea>`, `<summary>`, `<iframe>`, and anything with `contenteditable`. A `<div>`, `<span>`, `<h2>` or `<li>` is not, no matter what event handlers you attach to it. Separately, an element can be *programmatically focusable*: a call to `element.focus()` lands on it even though Tab never stops there. `tabindex` is the one attribute that moves an element between these states, and its three value classes map onto exactly that distinction. ## tabindex="0" — join the tab order in place The element becomes sequentially focusable and takes its position from the document: it is visited in DOM order alongside the native controls around it. Use it when you have built a control that has no native equivalent and it must be reachable by keyboard. What `tabindex="0"` grants is focusability and nothing else. It does not give the element a role, keyboard activation, or a disabled state — you supply those. And it is redundant on elements that are already focusable: `<button tabindex="0">` adds noise and nothing more. ## tabindex="-1" — focusable only from code A negative value removes the element from the Tab sequence while making it a valid target for `.focus()`. This is the workhorse of focus management: the target of a skip link, the heading you focus after the view changes, the container of a dialog that has no focusable child yet. Any negative integer behaves the same way; `-1` is simply the convention. One consequence surprises people: a `tabindex="-1"` element can still be focused by clicking it, which is why a focus ring can appear on click where you did not expect one. ## Positive values reorder the entire document The specified order is: all elements with a positive tabindex first, ascending by value, ties broken by DOM order; then everything with `0` or a default tabindex, in DOM order. So `tabindex="1"` anywhere in the page makes that element the very first Tab stop — ahead of the skip link, ahead of the site navigation. The values are global to the document, not scoped to a component. Two independently authored widgets that both ship `tabindex="2"` will interleave unpredictably, and inserting a control in the middle of a numbered sequence means renumbering everything after it. Accessibility linters flag any positive value for these reasons. ```html <button>Native — already in the tab order</button> <div tabindex="0">Reachable with Tab, in DOM order</div> <div tabindex="-1" id="panel">Only reachable via panel.focus()</div> <div tabindex="3">Visited before every element above</div> ``` ## What tabindex cannot override A `disabled` form control is not focusable, and adding `tabindex` does not change that. Content inside `display: none`, `visibility: hidden`, the `hidden` attribute, or an `inert` subtree is likewise unreachable — `tabindex` does not resurrect it. If the element is not rendered and not interactive, the attribute has nothing to act on. ## How to check it Put the mouse away and press Tab from the top of the page. The focus indicator should travel in the order you read the page. Anything that jumps to an unrelated corner is a positive tabindex; any control you can never reach is a missing one; any stop on a decorative element is a stray `tabindex="0"`. The fix is nearly always to delete the attribute and use the native element instead.

  • Which elements are already in the tab order without any tabindex attribute?
    Links with an `href`, `<button>`, `<input>`, `<select>`, `<textarea>`, `<summary>`, `<iframe>`, and anything with `contenteditable`. Adding `tabindex="0"` to those is redundant. Everything else — `div`, `span`, headings, list items — is not focusable until you add the attribute.
  • If a page has one element with tabindex="1" and two with tabindex="2", how does Tab order them against the rest of the page?
    The `1` comes first, then the two `2`s in DOM order, then every element with `0` or no tabindex in DOM order. Positive values are always visited before the natural sequence, which is exactly why they break pages.
  • Is there any behavioural difference between tabindex="-1" and tabindex="-5"?
    No. Any negative value means the same thing: out of the sequential tab order, still focusable programmatically and by click. `-1` is convention only, so use it consistently rather than encoding meaning in the number.

saying these in an interview costs you the question

  • Thinks tabindex numbers should follow the visual reading order
  • Says tabindex="-1" makes an element completely unfocusable
  • Believes a positive tabindex is scoped to its container
  • Adds tabindex="0" to buttons and links that are already focusable
  • Assumes tabindex alone supplies keyboard activation behaviour

context

open as a page

When a modal dialog opens on a page, what must happen to keyboard focus while it is open and when it closes?

level: seniorimportance: must knowfreq 56%

basics

~20 s

Move focus into the dialog when it opens, keep it inside while it is open, make the rest of the page inert so Tab cannot escape, and return focus to the control that opened the dialog once it closes.

open as a page

In HTML, what does the inert attribute do to the subtree it is applied to, and how does that differ from aria-hidden="true"?

level: middleimportance: should knowfreq 38%

basics

~10 s

inert removes a whole subtree from interaction: nothing inside can be focused or clicked, and it disappears from the accessibility tree. aria-hidden="true" only hides content from assistive technology, leaving it focusable and clickable.

open as a page

In a client-side-routed app where navigation swaps the page content without a full document load, why do keyboard and screen-reader users lose their place, and what markup makes the fix possible?

level: seniorimportance: should knowfreq 44%

basics

~20 s

The link that was clicked is removed along with the old view, so focus falls back to the document body: tabbing restarts at the top and nothing is announced. The fix is a focus target in the new view, such as a heading carrying tabindex="-1".

open as a page