@agentic-intelligence/dom-engine
Version:
Agentic DOM Intelligence - A lightweight TypeScript library for DOM analysis and manipulation, designed for web automation and AI agents
103 lines • 4.2 kB
JavaScript
/**
* Interactive elements finder
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getInteractiveSelectors = getInteractiveSelectors;
exports.findInteractiveElements = findInteractiveElements;
const helpers_1 = require("../utils/helpers");
const element_analyzer_1 = require("./element-analyzer");
/**
* Gets CSS selectors for interactive elements
* @returns Array of CSS selectors
*/
function getInteractiveSelectors() {
return [
// Form elements
'input:not([type="hidden"])',
'input[type="checkbox"]',
'textarea',
'select',
'button',
// Links
'a[href]',
'a[onclick]',
// Elements with click events
'[onclick]',
'[onmousedown]',
'[onmouseup]',
// Elements with interactive roles
'[role="button"]',
'[role="link"]',
'[role="menuitem"]',
'[role="tab"]',
'[role="option"]',
// Editable elements
'[contenteditable="true"]',
// Elements with tabindex (keyboard navigable)
'[tabindex]:not([tabindex="-1"])',
// Elements with pointer cursor
'[style*="cursor: pointer"]',
// Elements with custom events
'[data-action]',
'[data-toggle]',
'[data-target]'
];
}
/**
* Finds and categorizes visible interactive elements in the DOM
* @returns Object with categorized elements and total counter
*/
function findInteractiveElements() {
const selectors = getInteractiveSelectors().join(', ');
const allElements = document.body.querySelectorAll(selectors);
// Categorizers
const categorizers = {
buttons: (el) => el.constructor.name === 'HTMLButtonElement' || el.getAttribute('role') === 'button',
inputs: (el) => ['HTMLInputElement', 'HTMLTextAreaElement', 'HTMLSelectElement'].includes(el.constructor.name),
links: (el) => el.constructor.name === 'HTMLAnchorElement',
editable: (el) => el.contentEditable === 'true',
custom: (el) => !!el.onclick || !!el.getAttribute('onclick'),
selectable: () => true // fallback
};
// Process elements in a single pass
const categorized = Object.keys(categorizers).reduce((acc, key) => ({ ...acc, [key]: [] }), {});
let totalProcessed = 0;
for (const element of allElements) {
if (!(0, element_analyzer_1.isElementVisible)(element))
continue;
let textContent = (0, element_analyzer_1.getElementText)(element);
const isButtonWithSvgIcon = !textContent && (0, element_analyzer_1.hasSvgIcon)(element);
// Skip elements without text content, unless it's a button with SVG icon
if (!textContent && !isButtonWithSvgIcon)
continue;
// Handle button with SVG icon - assign text content
if (isButtonWithSvgIcon) {
textContent = '[This is an Icon Button]';
}
const domId = (0, helpers_1.generateUniqueId)();
element.setAttribute('agentic-purpose-id', domId);
const elementInfo = (0, helpers_1.filterValidProperties)({
text: textContent,
constructorName: element.constructor.name,
agenticPurposeId: domId,
type: element.type,
id: element.id?.substring(0, 40),
className: (0, helpers_1.filterStylingClasses)(element.className),
rect: element.getBoundingClientRect(),
onclick: element.onclick ? 'Yes' : 'No',
tabindex: element.tabIndex,
role: element.getAttribute('role'),
href: element.getAttribute('href'),
title: element.getAttribute('title'),
ariaLabel: element.getAttribute('aria-label'),
...(element.constructor.name === 'HTMLInputElement' && (0, element_analyzer_1.getSiblingText)(element))
});
// Find category
const category = Object.keys(categorizers).find(key => categorizers[key](element)) || 'selectable';
categorized[category].push(elementInfo);
totalProcessed++;
}
return { ...categorized, total: totalProcessed };
}
//# sourceMappingURL=interactive-finder.js.map
;