src/lib/form/extractors/selector-extractor.ts

Total Symbols
1
Lines of Code
54
Avg Complexity
7.0
Avg Coverage
84.2%

Symbols by Kind

function 1

All Symbols

Name Kind Visibility Status Lines Signature
getUniqueSelector function exported- 16-47 getUniqueSelector(element: Element): : string

Full Source

/**
 * Selector Extractor
 *
 * Builds a unique CSS selector for a given DOM element.
 * Used to identify fields reliably across page re-renders.
 */

import type { Extractor } from "./extractor.interface";

/**
 * Builds a unique, stable CSS selector for a DOM element.
 * Traverses parent chain using tag + `:nth-of-type`, shortcuts on `id`.
 * @param element - The element to build a selector for
 * @returns A CSS selector string that uniquely identifies the element
 */
export function getUniqueSelector(element: Element): string {
  if (element.id) return `#${CSS.escape(element.id)}`;

  const parts: string[] = [];
  let current: Element | null = element;

  while (current && current !== document.body) {
    let selector = current.tagName.toLowerCase();

    if (current.id) {
      selector = `#${CSS.escape(current.id)}`;
      parts.unshift(selector);
      break;
    }

    const parent: Element | null = current.parentElement;
    if (parent) {
      const siblings = Array.from(parent.children).filter(
        (c: Element) => c.tagName === current!.tagName,
      );
      if (siblings.length > 1) {
        const index = siblings.indexOf(current) + 1;
        selector += `:nth-of-type(${index})`;
      }
    }

    parts.unshift(selector);
    current = parent;
  }

  return parts.join(" > ");
}

/** Extractor object — wraps {@link getUniqueSelector} under the common Extractor contract. */
export const selectorExtractor: Extractor<Element, string> = {
  name: "selector",
  extract: getUniqueSelector,
};