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