Skip to main content

Events

Tabster communicates state changes and lets you hook into (or override) its keyboard handling via standard DOM CustomEvents. Every event bubbles, is cancelable, and is composed (crosses Shadow DOM boundaries). Event names and classes are exported from the package root:

import {
TabsterFocusInEventName,
TabsterFocusInEvent,
// ...and so on for every event below
} from "tabster";

All event classes extend an internal TabsterCustomEvent<D> base (itself extending CustomEvent<D>), so event.detail is typed per event — listed as "Detail" below. (event.details, with a trailing s, also exists for backwards compatibility — prefer detail.)

You can listen the same way you would for any other DOM event:

document.addEventListener(TabsterMoveFocusEventName, (e) => {
console.log(e.detail);
});

Core focus events

Name (constant)ClassDetailFired when
tabster:focusin (TabsterFocusInEventName)TabsterFocusInEventFocusedElementDetail (relatedTarget?, isFocusedProgrammatically?, modalizerId?)The focused element tracked by the Tabster core changes to an element.
tabster:focusout (TabsterFocusOutEventName)TabsterFocusOutEventFocusedElementDetailFocus leaves the element currently tracked by the Tabster core.
tabster:movefocus (TabsterMoveFocusEventName)TabsterMoveFocusEvent{ by, owner, next, relatedEvent? }Tabster is about to perform an interceptable focus move. Call preventDefault() to stop Tabster's proposed move and substitute your own.

TabsterMoveFocusEventDetail.by is "mover" | "groupper" | "modalizer" | "root" | "deloser", owner is the element that initiated the move, and next is the element Tabster intends to focus (null means "leave the Tabster root entirely", e.g. to the browser's own UI).

This is the general interception point for Tabster-initiated focus movement that exposes a proposed destination. Calling preventDefault() on the custom event tells Tabster not to perform that move; the listener can then focus its own destination. If a native keyboard action also needs to be suppressed, prevent its relatedEvent as well.

document.addEventListener("tabster:movefocus", (e) => {
if (e.detail?.relatedEvent?.key === "Enter") {
e.preventDefault();
e.detail.relatedEvent.preventDefault();
customElement.focus();
}
});

Mover events

Name (constant)ClassDetailFired when
tabster:mover:state (MoverStateEventName)MoverStateEventMoverElementState ({ isCurrent, visibility })A Mover item's tracked state changes (requires trackState/visibilityAware — see Mover).
tabster:mover:movefocus (MoverMoveFocusEventName)MoverMoveFocusEvent{ key: MoverKey }Dispatch this to a Mover element yourself to programmatically move its focus, as if a key had been pressed.
tabster:mover:memorized-element (MoverMemorizedElementEventName)MoverMemorizedElementEvent{ memorizedElement: HTMLElement | undefined }Dispatch this to a Mover element to forget or override its memorizeCurrent element.
document.addEventListener("tabster:mover:state", (e) => {
const target = e.composedPath()[0] as HTMLElement;
target.classList.toggle("current", !!e.detail?.isCurrent);
});

Groupper events

Name (constant)ClassDetailFired when
tabster:groupper:movefocus (GroupperMoveFocusEventName)GroupperMoveFocusEvent{ action: GroupperMoveFocusAction }Dispatch this to a Groupper element to programmatically enter (GroupperMoveFocusActions.Enter) or escape (.Escape) it.

Modalizer events

Name (constant)ClassDetailFired when
tabster:modalizer:active (ModalizerActiveEventName)ModalizerActiveEvent{ id, element }A Modalizer becomes active.
tabster:modalizer:inactive (ModalizerInactiveEventName)ModalizerInactiveEvent{ id, element }A Modalizer becomes inactive.
tabster:modalizer:focusin (ModalizerFocusInEventName)Reserved: exported as a name constant, but Tabster does not currently dispatch this event and there is no corresponding event class.
tabster:modalizer:focusout (ModalizerFocusOutEventName)Reserved: exported as a name constant, but Tabster does not currently dispatch this event and there is no corresponding event class.

Deloser events

Name (constant)ClassDetailFired when
tabster:deloser:focus-lost (DeloserFocusLostEventName)DeloserFocusLostEventDeloserElementActionsFocus was lost from a Deloser and Tabster couldn't automatically find where to restore it (or strategy: Manual was set — see Deloser). Handle this to implement custom restore logic.
tabster:deloser:restore-focus (DeloserRestoreFocusEventName)DeloserRestoreFocusEventundefinedDispatch this to a Manual-strategy Deloser element to trigger its restore.

Restorer events

Name (constant)ClassDetailFired when
tabster:restorer:restore-focus (RestorerRestoreFocusEventName)RestorerRestoreFocusEventundefinedA Restorer Source lost focus to <body> and is asking to restore it to the last known Target.

Root events

Name (constant)ClassDetailFired when
tabster:root:focus (RootFocusEventName)RootFocusEvent{ element }Focus enters a Tabster root.
tabster:root:blur (RootBlurEventName)RootBlurEvent{ element }Focus leaves a Tabster root.

Deprecated dispatch helpers

Three deprecated functions wrap element.dispatchEvent(new ...Event(...)) for the "dispatch to" events above — prefer dispatching the events directly:

Deprecated functionEquivalent to
dispatchMoverMoveFocusEvent(target, key)target.dispatchEvent(new MoverMoveFocusEvent({ key }))
dispatchMoverMemorizedElementEvent(target, el)target.dispatchEvent(new MoverMemorizedElementEvent({ memorizedElement: el }))
dispatchGroupperMoveFocusEvent(target, action)target.dispatchEvent(new GroupperMoveFocusEvent({ action }))