HTML autofocus: choose the first field without stealing focus later

Use the Boolean autofocus attribute for the intended starting control when a document loads, or when its containing dialog or popover opens. Keep one autofocus target in that scope. For a later, user-requested panel update, call focus() deliberately after the target exists. Adding autofocus to an arbitrary rerender is not a reliable focus-management policy. MDN autofocus reference

An equipment desk opens a full-page lookup at the start of each shift. Starting in the asset-code field saves a click. Once the operator has started entering a fault note, however, a refreshed lookup panel must not pull the cursor away from that note. Those are two different moments to handle.

Set the starting target in the markup

<label for="asset-code">Asset code</label>
<input id="asset-code" name="assetCode" autofocus>

The presence of the attribute enables it. autofocus="false" still enables autofocus; remove the attribute to turn it off. It is a global attribute, so it can also apply to a focusable button or an editable region.

Several components can accidentally contribute autofocus targets to the same document. Find them before changing timing:

console.table(
  [...document.querySelectorAll('[autofocus]')].map(element => ({
    tag: element.tagName,
    id: element.id,
    disabled: element.matches(':disabled')
  }))
);

Review the scope of each result. A target inside a closed dialog serves that dialog's opening; it is not a second page-load target to remove automatically. Within the relevant document, dialog, or popover scope, choose one intended target. If several have the attribute, the first focusable one receives focus.

Check whether a fragment already identifies a destination

A link such as /assets#fault-note expresses a destination. MDN documents that page-load autofocus does not take focus when the URL fragment identifies an element; focus generally goes to the identified element instead. Test the exact link that produced the report before diagnosing the asset field as broken.

Do not add a timer that repeatedly calls focus() until the field wins. That can override the destination or interrupt someone who has already moved to another control.

Move focus after an explicit panel-opening action

For an existing hidden editor panel, make its focus change part of the button's action:

<button id="edit-note" type="button">Edit fault note</button>
<section id="note-panel" hidden>
  <label for="fault-note">Fault note</label>
  <textarea id="fault-note" name="faultNote"></textarea>
</section>
const trigger = document.getElementById('edit-note');
const panel = document.getElementById('note-panel');
const note = document.getElementById('fault-note');

trigger.addEventListener('click', () => {
  panel.hidden = false;
  note.focus();
});

This example uses an already-created panel and a synchronous reveal. If a framework creates the textarea later, focus it in the framework's post-render or ref lifecycle after that particular opening action. Background refreshes should not run the opening action again.

focus({ preventScroll: true }) suppresses the usual scroll to the target. Use it only when keeping the current scroll position is appropriate and the person can still locate the focused field. It does not make a hidden, disabled, or inert target usable. HTMLElement.focus()

For a native dialog, put the autofocus target inside the dialog and let opening the dialog establish its initial focus. Keep the modal form's close and return path intact rather than applying page-load logic on every state change.

Verify who owns focus at each moment

Load the lookup page without a fragment and check the asset field. Open the fault-note panel with its button and check the textarea. Then type a note while the application refreshes lookup data; the cursor should stay in the note. Inspect document.activeElement when the actual result differs.

Repeat with the destination link and with a narrow touch-screen layout. Autofocus can scroll the page, reveal a software keyboard, and cause a screen reader to begin at the field without reading its preceding instructions. If the operator needs those instructions first, begin at the page's normal reading position and provide an explicit lookup action instead. The tabindex guide explains which additional targets can receive scripted focus.

Finish by removing any refresh-triggered focus calls that are unrelated to a user action. The lookup can update its data while the operator finishes the fault note.

Keep reading