Adding a Global Keyboard Shortcut Before Context Checks
TLDR: Global shortcuts that toggle application-wide state must be registered before context guards, not inside them. Alt+D fires the drag toggle before isTableContext() evaluates, so the shortcut works regardless of where focus lives.
Repo: tools/table-formatter
The Feature
Alt+D toggles drag-and-drop mode in TAFNE. When drag mode is on, cells can be dragged to reorder rows and columns. When it is off, dragging selects cell ranges instead.
Drag mode is application-wide state. It applies to the entire table, not to a specific cell or selection.
The Bug
The initial implementation put the Alt+D handler inside the isTableContext() guard:
document.addEventListener('keydown', (e) => {
if (!isTableContext()) return; // guard first
if (e.altKey && e.key === 'd') { toggleDragMode(); } });
isTableContext() returns true when keyboard focus is inside the table viewport. If the user's focus is on the toolbar, a modal, or any element outside the table, isTableContext() returns false and the early return fires. The Alt+D press is silently ignored.
This meant drag mode could only be toggled when the user had already clicked into a cell. Pressing Alt+D from the toolbar or from anywhere else had no effect.
Why This Matters
Drag mode is a modal toggle. Users activate it before interacting with the table, not during. They might:
- Enable drag mode from the toolbar
- Click into the table
- Drag a row
Alt+D requires table focus, step 1 is impossible via keyboard. The only path is clicking the toolbar button. Keyboard shortcuts that require the user to already be in the right context are broken shortcuts.
The Fix
Move global shortcuts before context guards:
document.addEventListener('keydown', (e) => {
// global shortcuts fire regardless of context
if (e.altKey && e.key === 'd') {
e.preventDefault();
toggleDragMode();
return;
}
// context-specific shortcuts follow if (!isTableContext()) return;
if (e.key === 'Delete') deleteSelectedCells(); if (e.ctrlKey && e.key === 'c') copySelected(); // ... });
Alt+D now fires from any focus state. The e.preventDefault() blocks the browser's default Alt+D behavior (which varies by browser but is generally nothing useful).
The Rule
Classify every shortcut before implementing it:
- Global state toggles (drag mode, dark mode, panel visibility): must fire before context guards
- Selection operations (copy, delete, transpose): need table context, fire after guard
- Cell editing shortcuts (Enter to confirm, Escape to cancel): need cell-editing context, fire in the cell's own event handler
Tradeoffs
Global shortcuts can conflict with browser defaults. Alt+D is relatively safe. Alt+F opens the browser's file menu in some browsers. Always call e.preventDefault() on global shortcuts and test across Chrome, Firefox, and Safari.
Global shortcuts require visible feedback. If Alt+D fires but no visual indicator changes, users cannot tell if the shortcut worked. TAFNE shows a toolbar button state change and a brief toast notification when drag mode toggles.