Restorer 
About
Restorer moves focus back to a known place when it's lost to <body> —
for example when the currently focused element (or an ancestor of it) is
removed from the DOM. It's a lighter-weight, more explicit alternative to
Deloser for the common "remember where to go back to" case:
instead of tracking a full history tree per container, you mark one element
as the source (the thing whose removal should trigger a restore) and one
or more elements as the target (where focus should go back to).
Setup
Call getRestorer() once to enable the restorer data-tabster key:
import { createTabster, getRestorer } from "tabster";
const tabster = createTabster(window);
getRestorer(tabster);
Properties
interface RestorerProps {
type: RestorerType; // RestorerTypes.Source | RestorerTypes.Target
id?: string;
}
type: RestorerTypes.Source— put this on the element (or a container around the elements) whose removal-while-focused should trigger a restore.type: RestorerTypes.Target— put this on the element focus should return to. Restorer keeps a small history (last 10) of focusedTargetelements, and picks the most recent one that's still in the DOM.id?: string— when set on both aSourceand one or moreTargetelements, restricts the restore to only considerTargets sharing thatid, instead of the global target history.
import { getTabsterAttribute, RestorerTypes } from "tabster";
<button {...getTabsterAttribute({ restorer: { type: RestorerTypes.Target } })}>
Open dialog
</button>;
<div {...getTabsterAttribute({ restorer: { type: RestorerTypes.Source } })}>
<button id="unmount">Close</button>
{/* dialog contents */}
</div>;
In this shape, focusing the "Open dialog" button records it as a target;
opening the dialog and then removing the Source container (for example by
un-rendering it) moves focus back to "Open dialog" automatically.
Caveats
- Restorer only acts when focus is actually lost — i.e. the active
element becomes
<body>. If focus already moved somewhere sensible (for example your own code focused something else before removing theSource), Restorer does nothing. - It also skips restoring when focus lands on
<body>because of a mouse click on empty space rather than because the focused element disappeared — Restorer checkskeyboardNavigation.isNavigatingWithKeyboard()internally to distinguish the two, unless theSourceelement is no longer in the DOM at all, in which case it always restores. - A
Sourceelement, when disposed (e.g. removed from the tree) while it still has focus within it, dispatches aRestorerRestoreFocusEvent. Restorer and Deloser use separate event types and restoration policies; Deloser'sManualstrategy is triggered withDeloserRestoreFocusEvent.
Examples
See Restorer examples in Storybook,
including a variant that restores from a second, more recently used
Target in history.