js/shortcuts.js — one listener for every key
In the code column — js/shortcuts.js. It's a perfect delegation example: one listener on document serves every keyboard shortcut. We don't add a listener per key — we let events bubble up to document and decide there what to do.
How it works: register('g', () => navigate('/gallery')) remembers a key; initShortcuts() listens for keydown on document and, on a match, calls the handler.
Two decisions matter here (both real UX questions):
- Don't steal keys while the user is typing.
isTyping(e.target)checks whether focus is in an input field. If so — we skip (so/in the search box types a slash instead of opening search). The exception isEscape, which should always "get out". - Don't fight the browser. If
Ctrl/Cmd/Altis held — skip, so we don't hijackCtrl+F,Cmd+Rand friends.
document.addEventListener('keydown', (e) => {
if (isTyping(e.target)) { if (e.key === 'Escape') e.target.blur(); return; }
if (e.metaKey || e.ctrlKey || e.altKey) return;
const entry = shortcuts.get(e.key.toLowerCase());
if (entry) { e.preventDefault(); entry.handler(e); }
});
Notice e.preventDefault() — when the key is ours, we stop the browser's default so that, say, / doesn't open the browser's own find bar.
Tip. This pattern — "one listener, a map of actions" — recurs everywhere: keyboard, list clicks, menus. Instead of adding N listeners (and cleaning them up), you add one on the parent. Less code, fewer memory leaks, and it works for content added dynamically too.