Focus History — Remembering and Restoring Focus Position
In this tutorial, you will learn about Focus History. We cover key concepts, practical examples, and best practices to help you master this topic.
Focus history tracks the sequence of focused elements so focus can be restored to previous positions when navigating back, closing overlays, or undoing actions.
In this tutorial, you'll learn Focus Management with focus history.
What You'll Learn
By the end of this lesson, you'll understand how to implement a focus history stack, restore focus on navigation, and handle complex focus scenarios.
Why It Matters
Users expect to return to their previous focus position when they close a dialog, navigate back, or undo an action. Focus history makes this possible.
Real-World Use
Doda Browser's multi-level settings panels use focus history so users return to the correct panel when navigating back from sub-settings.
Focus History Stack
flowchart LR A[Focus Stack] --> B[Push: element focused] A --> C[Pop: return to previous] B --> D[Stack grows] C --> E[Stack shrinks] D --> F[Top: current focus] E --> F
Implementing Focus History
class FocusHistory {
constructor(maxSize = 10) {
this.stack = [];
this.maxSize = maxSize;
}
push(element) {
this.stack.push(element);
if (this.stack.length > this.maxSize) {
this.stack.shift();
}
}
pop() {
const element = this.stack.pop();
if (element && document.contains(element)) {
element.focus();
return true;
}
return false;
}
peek() {
return this.stack[this.stack.length - 1] || null;
}
clear() {
this.stack = [];
}
}
const focusHistory = new FocusHistory();
// Track focus changes
document.addEventListener('focusin', (event) => {
focusHistory.push(event.target);
});
Using Focus History
// When a dialog opens, save the current focus
function openPanel(trigger) {
focusHistory.push(trigger);
// Open the panel
}
// When the user navigates back
function handleBack() {
if (!focusHistory.pop()) {
// No history, go to a safe default
document.querySelector('main h1').focus();
}
}
// When a modal closes
function closeDialog(trigger) {
dialog.hidden = true;
if (document.contains(trigger)) {
trigger.focus();
} else {
focusHistory.pop();
}
}
Focus History for SPAs
// Track focus per route
class SPARouter {
constructor() {
this.routeHistory = {};
this.currentRoute = null;
}
navigate(route) {
// Save current focus for the previous route
this.routeHistory[this.currentRoute] = document.activeElement;
// Load new route
this.currentRoute = route;
loadContent(route);
// Restore focus for the new route
const savedFocus = this.routeHistory[route];
if (savedFocus && document.contains(savedFocus)) {
savedFocus.focus();
} else {
document.querySelector('main h1').focus();
}
}
}
Common Mistakes
- Not clearing history when appropriate: History accumulates stale references.
- Storing references to removed elements: Focus restoration to a removed element fails.
- Not capping the history size: Memory leaks from infinite history.
- Over-focusing elements: Not every focus change needs to be in the history.
- Restoring focus to hidden elements: Elements hidden or disabled should not receive focus.
Practice and Challenge
1. What is focus history used for? Tracking focus positions for restoring them after navigation or overlay dismissal.
2. How does a focus history stack work? Elements are pushed as focus changes and popped when restoring previous positions.
3. What happens if you pop an element that no longer exists? The pop fails. Provide a fallback focus target.
4. Why should you cap the history size? To prevent memory leaks from unbounded history growth.
5. Challenge: Implement focus history for a multi-step form. Each step saves focus and restores it when navigating back.
FAQ
Mini Project
Build a multi-panel settings interface with navigation history. Users should return to the correct panel and element when clicking Back.
What's Next
Continue to Inert Attribute to learn about making content unfocusable.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro