focus-on-modal-closed
Web validation
Opening a modal dialog moves focus into it. When the dialog closes, focus has to go somewhere again, and the page should decide where, instead of leaving it to chance. In most cases the right place is the element that opened the dialog, so users can continue from exactly where they were.
If focus is not managed, it may land at the top of the page, on the <body>, or on an element that no longer exists. Keyboard users, especially those with low vision, may struggle to find where focus is and have to navigate all the way back to where they were. Screen reader users have to work out their location in the page again before they can continue.
Pass Patterns
Focus returns to the triggering button
The script stores the element that opened the dialog and moves focus back to it when the dialog closes.
<button id="open-settings">Settings</button>
<div role="dialog" aria-modal="true" aria-labelledby="settings-title" hidden>
<h2 id="settings-title">Settings</h2>
<button id="close-settings">Close</button>
</div>
<script>
const trigger = document.getElementById("open-settings");
const dialog = document.querySelector('[role="dialog"]');
trigger.addEventListener("click", () => {
dialog.hidden = false;
document.getElementById("close-settings").focus();
});
document.getElementById("close-settings").addEventListener("click", () => {
dialog.hidden = true;
trigger.focus(); // Return focus to the element that opened the dialog
});
</script>Native <dialog> returns focus automatically
A native <dialog> opened with showModal() returns focus to the previously focused element when it closes.
<button id="open-help">Help</button>
<dialog id="help-dialog" aria-labelledby="help-title">
<h2 id="help-title">Help</h2>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
<script>
document.getElementById("open-help").addEventListener("click", () => {
document.getElementById("help-dialog").showModal();
});
</script>Fail Patterns
Focus is lost when the dialog closes
The dialog is hidden, but focus is never moved, so it falls back to the <body> and users lose their place.
<button id="open-settings">Settings</button>
<div role="dialog" aria-modal="true" aria-labelledby="settings-title" hidden>
<h2 id="settings-title">Settings</h2>
<button id="close-settings">Close</button>
</div>
<script>
document.getElementById("close-settings").addEventListener("click", () => {
document.querySelector('[role="dialog"]').hidden = true;
// Focus is not moved back to the "Settings" button
});
</script>Try It Yourself
Using a keyboard
- Move focus to the control that opens the modal dialog, and press Enter or Space to open it.
- Close the dialog with its Close button or the Escape key.
- Watch where focus lands.
Expected results: Focus returns to the control that opened the dialog, or to another logical element nearby.
Actual results: Focus moves to the top of the page, disappears, or lands on an unrelated element.
Using a screen reader
New to screen reader testing, or need a refresher?
Review this article first: Practical guides for using screen readers
- Load the page and open a screen reader.
- Open the modal dialog, then close it.
- Listen to what the screen reader announces after the dialog closes.
Expected results: The screen reader announces the control that opened the dialog, or another logical element nearby.
Actual results: The screen reader announces the page title or an unrelated element, or says nothing, leaving the user unsure where they are.
FAQ
How does this issue relate to 2.4.3 Focus Order?
SC 2.4.3 requires that focusable components receive focus in an order that preserves meaning and operability. Opening and closing a dialog is part of that sequence: when the dialog closes, focus should return to the point in the page where the user left off. If focus jumps to the top of the page or is lost, the sequence is broken and users must find their place again.
Why is this issue marked as Moderate?
Users can usually recover by navigating back to where they were, so the issue rarely blocks a task outright. However, losing focus after every dialog adds effort and disorientation, especially for keyboard and screen reader users, so it is rated moderate.
Must focus always return to the element that opened the dialog?
Not always. Returning to the trigger is the usual and expected behavior, but sometimes it is not possible or not logical. For example, if the dialog deleted the item that contained the trigger, or if the dialog's action moves the user to the next step of a process. In those cases, move focus to the element that makes the most sense for the next step, such as the next item in the list or the heading of the new step.
Do native dialog elements handle this automatically?
Yes. A native <dialog> opened with showModal() returns focus to the element that was focused before it opened. Custom modals built with role="dialog" need a script to store the trigger and move focus back to it.
Standards and Regulations
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.4.3 Focus Order (A) | If a web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability. |
| EN 301 549 | 9.2.4.3 Focus Order | Same as the WCAG text. |
| 508 Trusted Tester | 2.4.3-focus-order-return, 4.H | Focus is returned to the logical sequence. |