Representative interview topic

Frontend Interview: How do you use HTML command and commandfor accessibly?

FrontendHard
Offer.cc Editorial TeamPublished Updated

Question

You need a settings button that opens a dialog and closes it without duplicating component state synchronization. Explain how command and commandfor create declarative control, including custom commands, keyboard accessibility, and fallback for older browsers.

Prompt and Applicable Context

You need a settings button that opens a dialog and closes it without duplicating component state synchronization. Explain how command and commandfor create declarative control, including custom commands, keyboard accessibility, and fallback for older browsers.

The Invoker Commands API lets a button target an element ID and invoke a built-in action or dispatch a custom command event. It expresses who invokes and which element acts in HTML, but it does not replace business state machines, permission checks, or unsupported-browser fallbacks.

What the Interviewer Evaluates

Cover the same-tree target requirement for commandfor, built-in versus custom commands, event cancellation and default behavior, keyboard and focus management, and progressive enhancement instead of treating a new attribute as the only runtime guarantee.

Clarifying Questions

Confirm whether the target is a modal dialog, non-modal popover, or ordinary element; whether the button and target share a tree; whether submit validation is required; and which browsers matter. Also clarify initial focus, Escape behavior, focus return, and whether custom commands need business authorization.

30-Second Answer Framework

“I would point commandfor at the target and use built-in commands for show, hide, or toggle. Business actions use a custom command event where a controller validates state and may prevent default behavior. Native buttons are keyboard reachable, but I would still verify focus entry, Escape, focus return, and accessible names. Older browsers keep a JavaScript or component fallback that calls the same state machine, so native and fallback paths remain equivalent.”

Step-by-Step Deep Dive

Step 1: Declare the button-to-target relationship

The commandfor value is the target element's ID, and both elements must be in the same tree. The button's command value chooses the action. Static HTML can therefore express control without manual queries and duplicated state synchronization.

Step 2: Prefer built-in commands

Dialogs and popovers define browser commands for showing, hiding, and toggling. Native behavior can cooperate with top-layer, light-dismiss, and focus rules. Do not reimplement an existing semantic with a custom string.

html
<button commandfor="settings" command="show-modal">
  Open settings
</button>
<dialog id="settings">
  <button commandfor="settings" command="close">Close</button>
</dialog>

Step 3: Handle custom command events

Custom commands beginning with -- dispatch a command event containing the target and command value. The handler should validate origin and current state, call preventDefault() when needed, and run the business action. A custom command does not automatically change visibility.

Step 4: Keep authorization and validation in business code

Declarative invocation expresses intent; it cannot bypass authorization, form validation, or asynchronous persistence. A target controller should check permission, data version, and current state before acting, and return a recoverable error instead of treating HTML as a security boundary.

Step 5: Design focus and keyboard behavior

For native dialogs and popovers, verify initial focus, Escape dismissal, focus return, and the accessible name. Ordinary elements do not acquire these semantics automatically. A custom target needs an explicit keyboard model and focus policy.

Step 6: Handle cancellation and races

Several buttons can issue commands at once. Check the target's current state to prevent duplicate opening, submission, or stale asynchronous reopening after close. Keep async business state separate from visible state so failures cannot leave incorrect ARIA or focus state.

Step 7: Provide an older-browser fallback

Browsers that do not support commandfor ignore the attributes, so retain a normal button listener or component binding. Reuse the same command handler and accessibility tests instead of maintaining two drifting behaviors.

Step 8: Test native and fallback paths

Test mouse, keyboard, screen reader, Escape, focus return, duplicate commands, canceled defaults, denied permission, and async failure. Use a browser matrix for built-in commands, custom events, and target types; a MDN Baseline label is not a guarantee for every runtime.

High-Quality Sample Answer

I would point a static button with commandfor at a dialog in the same tree and use built-in show-modal and close commands for basic behavior. Business logic would use a custom command event whose handler checks authorization, form version, and current state and can prevent default behavior; the custom command itself does not toggle visibility. The native dialog supplies modal semantics, but I would still test focus, Escape, focus return, and the accessible name. Older browsers ignore the new attributes, so a fallback listener calls the same command handler. Finally, test keyboard and screen-reader behavior, concurrent clicks, async failures, and a browser matrix across both paths.

Common Mistakes

Assuming commandfor searches across a Shadow DOM or document

The specification requires the target ID in the same tree. Cross-tree or cross-document control needs an explicit component protocol or messaging, not a different ID.

Assuming every custom command has default behavior

Custom commands primarily dispatch an event. The handler must implement the state change and define when default behavior is canceled.

Testing only mouse clicks

Keyboard reachability, focus order, Escape, and screen-reader feedback are part of the interaction contract. A visual open state is not complete accessibility evidence.

Follow-Up Questions and Responses

Does a dynamic target replacement require rebinding commandfor?

If the replacement keeps the same ID and remains in the same tree, the declarative relationship can continue. The controller must still reset focus and business state. Duplicate IDs or a missing target should block the command and expose a diagnosable state.

How do you prevent duplicate submissions from a custom async command?

Use request state or an idempotency key to lock the same action, mark the button busy, and keep a cancel or retry path. After completion, confirm that the target still exists so an old result cannot mutate a new session.

When should a JavaScript component remain instead of using only HTML commands?

Use a component when the interaction needs cross-tree coordination, complex animation, async state machines, or broad legacy-browser support. command can still be a progressive-enhancement entry point, but the business boundary must remain testable.

Public sources

Related questions