Skip to content

Element Targeting

The current implementation does not persist a multi-strategy selector chain for replay. Navvy targets elements through the DOM tree generated for the current page state.

The content-side PageController builds a flat DOM tree and stores interactive elements in a selectorMap:

selectorMap: Map<number, InteractiveElementDomNode>

When the model chooses an action such as click_element_by_index, it passes an integer index. The action resolves that index against the current selectorMap and acts on the live DOM reference.

const element = getElementByIndex(selectorMap, index)
await clickElement(element)

This means indexes are valid for the current scanned browser state. After a page update, Navvy rescans and receives a new compact browser state before choosing the next action.

The DOM tree code does inspect semantics that can help decide whether an element is interactive:

  • Native tags such as a, button, input, select, textarea, details, summary, and label.
  • ARIA roles and states such as button, link, textbox, combobox, aria-expanded, and aria-checked.
  • Attributes such as data-testid, data-cy, and data-test when determining whether a node is distinct.

Those signals are used for detection and serialization. They are not currently composed into a persisted fallback selector hierarchy such as id -> name -> role -> structural path.

If an index cannot be resolved, the action fails and the agent receives the error through the tool result. The next step can rescan the page and choose a different index.

scan current DOM -> serialize indexed elements -> model selects index -> resolve in selectorMap -> execute

This design is simple and traceable, but it is different from a recorded workflow selector engine. Persistent selectors for saved task replay are planned separately.

The DOM tree implementation contains XPath helper code, but XPath emission is disabled in the current serialization path. The compact representation focuses on readable HTML-like lines with highlight indexes instead.