0
# Focus Management
1
2
Focus trapping utility for managing focus within modal components like dialogs, drawers, and popups. Ensures keyboard navigation stays within the modal and provides accessible focus management.
3
4
## Capabilities
5
6
### FocusTrap Class
7
8
Utility class that traps focus within a given root element, intended for modal components.
9
10
```typescript { .api }
11
/**
12
* Utility to trap focus in a given root element, e.g. for modal components such
13
* as dialogs. The root should have at least one focusable child element,
14
* for setting initial focus when trapping focus.
15
* Also tracks the previously focused element, and restores focus to that
16
* element when releasing focus.
17
*/
18
class FocusTrap {
19
constructor(root: HTMLElement, options?: FocusOptions);
20
trapFocus(): void;
21
releaseFocus(): void;
22
}
23
```
24
25
### Constructor
26
27
Create a new FocusTrap instance for a root element.
28
29
```typescript { .api }
30
/**
31
* Creates a new FocusTrap instance
32
* @param root - The root element to trap focus within
33
* @param options - Configuration options for focus behavior
34
*/
35
constructor(root: HTMLElement, options?: FocusOptions);
36
```
37
38
### Trap Focus
39
40
Activate focus trapping within the root element.
41
42
```typescript { .api }
43
/**
44
* Traps focus in root. Also focuses on either initialFocusEl if set;
45
* otherwise sets initial focus to the first focusable child element.
46
* @throws Error if the root element has no focusable children
47
*/
48
trapFocus(): void;
49
```
50
51
### Release Focus
52
53
Deactivate focus trapping and restore previous focus.
54
55
```typescript { .api }
56
/**
57
* Releases focus from root. Also restores focus to the previously focused
58
* element.
59
*/
60
releaseFocus(): void;
61
```
62
63
## Configuration Options
64
65
```typescript { .api }
66
interface FocusOptions {
67
/**
68
* The element to focus initially when trapping focus.
69
* Must be a child of the root element.
70
*/
71
initialFocusEl?: HTMLElement;
72
73
/**
74
* Whether to skip initially focusing on any element when trapping focus.
75
* By default, focus is set on the first focusable child element of the root.
76
* This is useful if the caller wants to handle setting initial focus.
77
*/
78
skipInitialFocus?: boolean;
79
80
/**
81
* Whether to restore focus on the previously focused element when releasing
82
* focus. This is useful if the caller wants to handle restoring focus.
83
*/
84
skipRestoreFocus?: boolean;
85
}
86
```
87
88
## Usage Examples
89
90
### Basic Modal Dialog
91
92
```typescript
93
import { FocusTrap } from '@material/dom/focus-trap';
94
95
// Create modal dialog
96
const dialog = document.querySelector('.dialog');
97
const focusTrap = new FocusTrap(dialog);
98
99
// Show modal and trap focus
100
dialog.classList.add('open');
101
focusTrap.trapFocus();
102
103
// Close modal and restore focus
104
function closeDialog() {
105
focusTrap.releaseFocus();
106
dialog.classList.remove('open');
107
}
108
```
109
110
### Modal with Custom Initial Focus
111
112
```typescript
113
import { FocusTrap } from '@material/dom/focus-trap';
114
115
const modal = document.querySelector('.modal');
116
const primaryButton = modal.querySelector('.primary-button');
117
118
const focusTrap = new FocusTrap(modal, {
119
initialFocusEl: primaryButton
120
});
121
122
// Show modal - focus will go to primary button
123
modal.style.display = 'block';
124
focusTrap.trapFocus();
125
```
126
127
### Skip Automatic Focus Management
128
129
```typescript
130
import { FocusTrap } from '@material/dom/focus-trap';
131
132
const drawer = document.querySelector('.drawer');
133
const focusTrap = new FocusTrap(drawer, {
134
skipInitialFocus: true, // Handle initial focus manually
135
skipRestoreFocus: true // Handle focus restoration manually
136
});
137
138
// Show drawer
139
drawer.classList.add('open');
140
focusTrap.trapFocus();
141
142
// Manually set focus
143
drawer.querySelector('.first-item').focus();
144
145
// Close drawer
146
focusTrap.releaseFocus();
147
drawer.classList.remove('open');
148
149
// Manually restore focus
150
document.querySelector('.menu-button').focus();
151
```
152
153
## Accessibility Features
154
155
### Focusable Element Detection
156
157
FocusTrap automatically detects focusable elements including:
158
- Elements with `tabindex` attribute
159
- Elements with `autofocus` attribute
160
- Interactive elements: `a`, `input`, `textarea`, `select`, `button`
161
- Elements that are visible and not disabled
162
- Elements not marked as `aria-hidden="true"`
163
164
### Sentinel Elements
165
166
The focus trap uses hidden "sentinel" elements to detect when focus attempts to leave the trapped area:
167
- Sentinels are placed at the beginning and end of the trapped region
168
- When a sentinel receives focus, it redirects to the appropriate boundary
169
- Sentinels are marked with `aria-hidden="true"` for screen readers
170
171
### Screen Reader Support
172
173
- Maintains proper focus order for screen reader navigation
174
- Doesn't interfere with screen reader virtual cursor
175
- Properly handles `aria-hidden` and `aria-disabled` attributes
176
- Works with assistive technology focus management
177
178
### Keyboard Navigation
179
180
- Supports Tab and Shift+Tab navigation
181
- Wraps focus from last to first element and vice versa
182
- Respects custom tab order via `tabindex` values
183
- Handles complex focusable element hierarchies
184
185
## Error Handling
186
187
```typescript
188
// FocusTrap will throw an error if root has no focusable children
189
const emptyDiv = document.createElement('div');
190
const focusTrap = new FocusTrap(emptyDiv);
191
192
try {
193
focusTrap.trapFocus();
194
} catch (error) {
195
console.error('FocusTrap: Element must have at least one focusable child.');
196
}
197
```