Manual UI Behaviors DOM helpers

DOM helpers

elements man ui/behaviors/dom

The 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.

Signatures
function 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.ehtml
import { 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).