DOM helpers
elements man ui/behaviors/domThe queries the behaviors share, exported for a template's own key and pointer handling: which elements take focus, whether one is disabled or visible, how to focus without scrolling, and the text a typeahead matches. Every function touches the DOM only when called, so importing them on the server is safe.
Signaturesfunction tabbables(container: Element): HTMLElement[]; function isDisabled(el: Element): boolean; function isVisible(el: Element): boolean; function hiddenWithRoot(el: Element | null | undefined, root: Element): boolean; function focusElement(el: HTMLElement | null | undefined, opts?: { select?: boolean }): boolean; function focusFirst(candidates: HTMLElement[], opts?: { select?: boolean }): HTMLElement | null; function clickPointer(e: MouseEvent, on?: Element | null): string; function isInside(target: EventTarget | null, roots: (Element | null | undefined)[]): boolean; function directionOf(el: Element): "ltr" | "rtl"; function textValue(el: Element): string;
| Function | What it returns |
|---|---|
tabbables(container) |
Every element Tab can reach in container, in DOM order: focusable, not disabled (a disabled fieldset included), tabindex >= 0, visible, not inert. Radio groups are not collapsed to one stop. |
isDisabled(el) |
True for an element the user cannot act on: disabled (a disabled fieldset included), aria-disabled="true" or data-disabled. |
isVisible(el) |
True when the element is in the document, renders a box and is not visibility: hidden. |
hiddenWithRoot(el, root) |
True when el is inside root and root itself renders no box (a closed Collapsible around a group). Such an element is hidden, not gone, so a focus stop on it is kept. |
focusElement(el, { select }) |
Focuses el without scrolling the page, selects a text input's content when asked, and returns whether el took focus. Null is a false, not an error. |
focusFirst(candidates, { select }) |
Focuses the first candidate that accepts focus and returns it, or null. |
clickPointer(e, on) |
The pointer behind a click: "mouse", "touch", "pen", or "" for a click from the keyboard, a label or code. When the click event does not say, the press that came before it decides, if it landed inside on (the element that took the click, by default). |
isInside(target, roots) |
True when target is inside any of the roots. Null entries are ignored. |
directionOf(el) |
The writing direction in effect on el. |
textValue(el) |
The text a typeahead matches: data-text-value, else the trimmed text content. |
In a template of your own
app/shared/templates/link-card/index.ehtmlimport { tabbables, isInside } from "@elements/ui/behaviors"; function onCardClick(e: MouseEvent, href: string) { let card = e.currentTarget as HTMLElement; if (isInside(e.target, tabbables(card))) { return; } location.assign(href); } <LinkCard (href: string, title: string)> <article class="link-card" onclick={(e) => onCardClick(e, href)}> <h3><a href={href}>{title}</a></h3> <slot/> </article> </LinkCard>
A click anywhere on the card follows the link, and a click on a control inside it (the link itself, a button in the slot) is left to that control.
Used by
tabbables: Dialog (opens in a new tab), Drawer (opens in a new tab),
NavigationMenu (opens in a new tab), Popover (opens in a new tab),
Resizable (opens in a new tab), VirtualList (opens in a new tab).
isDisabled: Combobox (opens in a new tab), Command (opens in a new tab),
the menus, Select (opens in a new tab). focusElement: the menus,
NavigationMenu (opens in a new tab), Popover (opens in a new tab),
Resizable (opens in a new tab), Tree (opens in a new tab).
clickPointer: Chart (opens in a new tab), Dialog (opens in a new tab),
the menus, Select (opens in a new tab). directionOf: every template with
a reading direction, from Accordion (opens in a new tab) to
Slider (opens in a new tab). textValue: Combobox (opens in a new tab),
Kanban (opens in a new tab). isInside: TagsInput (opens in a new tab).