0
# Keyboard Utilities
1
2
Cross-browser keyboard event normalization utilities that provide consistent key handling across different browsers and platforms.
3
4
## Capabilities
5
6
### Key Constants
7
8
Normalized string values for common keyboard keys.
9
10
```typescript { .api }
11
const KEY: {
12
readonly UNKNOWN: 'Unknown';
13
readonly BACKSPACE: 'Backspace';
14
readonly ENTER: 'Enter';
15
readonly SPACEBAR: 'Spacebar';
16
readonly PAGE_UP: 'PageUp';
17
readonly PAGE_DOWN: 'PageDown';
18
readonly END: 'End';
19
readonly HOME: 'Home';
20
readonly ARROW_LEFT: 'ArrowLeft';
21
readonly ARROW_UP: 'ArrowUp';
22
readonly ARROW_RIGHT: 'ArrowRight';
23
readonly ARROW_DOWN: 'ArrowDown';
24
readonly DELETE: 'Delete';
25
readonly ESCAPE: 'Escape';
26
readonly TAB: 'Tab';
27
};
28
```
29
30
### Key Normalization
31
32
Returns a normalized string for keyboard events that works consistently across browsers.
33
34
```typescript { .api }
35
/**
36
* Returns the normalized string for a navigational action derived from KeyboardEvent
37
* to be standard across browsers
38
* @param evt - The keyboard event to normalize
39
* @returns Normalized key string or 'Unknown' if not recognized
40
*/
41
function normalizeKey(evt: KeyboardEvent): string;
42
```
43
44
### Navigation Event Detection
45
46
Determine if a keyboard event represents a navigation action.
47
48
```typescript { .api }
49
/**
50
* Returns whether the event is a navigation event (Page Up, Page Down, Home, End, Arrow keys)
51
* @param evt - The keyboard event to test
52
* @returns True if the event is a navigation event
53
*/
54
function isNavigationEvent(evt: KeyboardEvent): boolean;
55
```
56
57
## Usage Examples
58
59
### Basic Key Handling
60
61
```typescript
62
import { KEY, normalizeKey } from '@material/dom/keyboard';
63
64
document.addEventListener('keydown', (event) => {
65
const key = normalizeKey(event);
66
67
switch (key) {
68
case KEY.ENTER:
69
case KEY.SPACEBAR:
70
// Activate element
71
handleActivation();
72
break;
73
case KEY.ESCAPE:
74
// Close modal/menu
75
closeModal();
76
break;
77
case KEY.ARROW_UP:
78
case KEY.ARROW_DOWN:
79
// Navigate list
80
navigateList(key === KEY.ARROW_UP ? -1 : 1);
81
break;
82
}
83
});
84
```
85
86
### Navigation Event Handling
87
88
```typescript
89
import { isNavigationEvent, normalizeKey, KEY } from '@material/dom/keyboard';
90
91
function handleKeydown(event: KeyboardEvent) {
92
if (isNavigationEvent(event)) {
93
event.preventDefault();
94
95
const key = normalizeKey(event);
96
switch (key) {
97
case KEY.ARROW_LEFT:
98
case KEY.ARROW_RIGHT:
99
handleHorizontalNavigation(key === KEY.ARROW_RIGHT ? 1 : -1);
100
break;
101
case KEY.ARROW_UP:
102
case KEY.ARROW_DOWN:
103
handleVerticalNavigation(key === KEY.ARROW_DOWN ? 1 : -1);
104
break;
105
case KEY.HOME:
106
focusFirst();
107
break;
108
case KEY.END:
109
focusLast();
110
break;
111
case KEY.PAGE_UP:
112
case KEY.PAGE_DOWN:
113
handlePageNavigation(key === KEY.PAGE_DOWN ? 1 : -1);
114
break;
115
}
116
}
117
}
118
```
119
120
### Menu Navigation
121
122
```typescript
123
import { KEY, normalizeKey, isNavigationEvent } from '@material/dom/keyboard';
124
125
class MenuComponent {
126
private currentIndex = 0;
127
private menuItems: HTMLElement[] = [];
128
129
handleKeydown(event: KeyboardEvent) {
130
const key = normalizeKey(event);
131
132
if (key === KEY.ESCAPE) {
133
this.closeMenu();
134
return;
135
}
136
137
if (key === KEY.ENTER || key === KEY.SPACEBAR) {
138
this.selectCurrentItem();
139
return;
140
}
141
142
if (isNavigationEvent(event)) {
143
event.preventDefault();
144
145
switch (key) {
146
case KEY.ARROW_UP:
147
this.moveFocus(-1);
148
break;
149
case KEY.ARROW_DOWN:
150
this.moveFocus(1);
151
break;
152
case KEY.HOME:
153
this.focusItem(0);
154
break;
155
case KEY.END:
156
this.focusItem(this.menuItems.length - 1);
157
break;
158
}
159
}
160
}
161
162
private moveFocus(direction: number) {
163
this.currentIndex = Math.max(0, Math.min(
164
this.menuItems.length - 1,
165
this.currentIndex + direction
166
));
167
this.focusItem(this.currentIndex);
168
}
169
}
170
```
171
172
### Form Validation
173
174
```typescript
175
import { KEY, normalizeKey } from '@material/dom/keyboard';
176
177
function handleFormKeydown(event: KeyboardEvent) {
178
const key = normalizeKey(event);
179
180
if (key === KEY.ENTER) {
181
const target = event.target as HTMLElement;
182
183
if (target.tagName === 'TEXTAREA') {
184
// Allow line breaks in textarea
185
return;
186
}
187
188
if (target.tagName === 'INPUT') {
189
// Submit form or move to next field
190
event.preventDefault();
191
submitFormOrFocusNext();
192
}
193
}
194
195
if (key === KEY.ESCAPE) {
196
// Cancel form editing
197
cancelFormEditing();
198
}
199
}
200
```
201
202
## Cross-Browser Compatibility
203
204
The keyboard utilities handle differences between browsers:
205
206
### Modern Browsers
207
- Use the standard `KeyboardEvent.key` property when available
208
- Return consistent string values per W3C specification
209
210
### Legacy Browsers
211
- Fall back to `KeyboardEvent.keyCode` property
212
- Map numeric key codes to standard key strings
213
- Handle vendor-specific key code variations
214
215
### Key Mapping
216
217
The normalization handles these browser differences:
218
219
| Key | Modern (`event.key`) | Legacy (`event.keyCode`) |
220
|-----|---------------------|-------------------------|
221
| Enter | `'Enter'` | `13` |
222
| Space | `' '` → `'Spacebar'` | `32` |
223
| Arrow Up | `'ArrowUp'` | `38` |
224
| Arrow Down | `'ArrowDown'` | `40` |
225
| Escape | `'Escape'` | `27` |
226
227
### Navigation Keys
228
229
The following keys are considered navigation events:
230
- `PAGE_UP`, `PAGE_DOWN` - Page navigation
231
- `HOME`, `END` - Document/container boundaries
232
- `ARROW_LEFT`, `ARROW_RIGHT` - Horizontal navigation
233
- `ARROW_UP`, `ARROW_DOWN` - Vertical navigation
234
235
### Unknown Keys
236
237
Keys that aren't recognized return `KEY.UNKNOWN` to allow graceful handling of:
238
- Unmapped key codes in legacy browsers
239
- New keys not in the normalization table
240
- Platform-specific keys