Skip to content

Form extraction

The FormExtractor utility analyzes a DOM element and reports the form elements found inside it, along with enough metadata (selector, label, value, options, state) to target and fill them programmatically.

// Through the forms namespace (recommended)
const formData = window.agentlet.forms.extract(element, options);
// Or through the extractor directly
const extractor = window.agentlet.forms.extractor;
const formData = extractor.extractFormStructure(element, options);
const formElement = document.getElementById('my-form');
const formData = window.agentlet.forms.extract(formElement);
const options = {
includeHidden: false, // Include hidden form fields
includeDisabled: false, // Include disabled form fields
includeReadOnly: true, // Include read-only form fields (default true)
includeBoundingBoxes: false, // Include element positioning
};
const formData = window.agentlet.forms.extract(element, options);

Extra keys are accepted and forwarded to the internal element extraction, but only the keys above are read by the current implementation.

{
metadata: {
tagName: 'div',
id: 'registration-form',
className: 'form-container',
url: 'https://example.com/register',
title: 'Registration page',
},
forms: [
{
type: 'form',
element: { /* a FormElementInfo describing the <form> itself */ },
elements: [ /* FormElementInfo, one per field in this form */ ],
},
],
elements: [ /* FormElementInfo, fields with no enclosing <form> */ ],
extractedAt: '2024-01-15T10:30:00.000Z',
}

Each field, in forms[].elements or the top-level elements array, is a FormElementInfo:

{
tagName: 'input',
type: 'email',
id: 'user-email',
name: 'email',
className: 'form-control required',
selector: '#user-email', // Single best selector for this element
attributes: { type: 'email', name: 'email', required: 'true' },
value: 'user@example.com', // Shape depends on the field type, see below
placeholder: 'Enter your email',
required: true,
disabled: false,
readonly: false,
visible: true,
interactable: true,
label: 'Email address', // Or null if no label was found
options: null, // Populated for select/radio/checkbox, see below
// boundingBox is only present when includeBoundingBoxes is true:
// { x, y, width, height, visible }
}
  • Checkbox or radio: { checked: boolean, value: string }
  • Select: { selectedValue: string, selectedOptions: Array<{ value, text }> }
  • File input: { files: string[], accept: string }
  • Everything else (text, email, textarea, …): a plain string, or null
  • Select: { multiple: boolean, options: Array<{ index, value, text, selected, disabled }> }
  • Radio or checkbox group: { group: Array<{ index, value, checked, label }>, groupSize: number }
  • Everything else: null

See Select options for a full walkthrough of the select and group shapes.

const formElement = document.getElementById('registration-form');
const formData = window.agentlet.forms.extract(formElement, {
includeHidden: true,
includeBoundingBoxes: true,
});
formData.forms[0].elements.forEach((element) => {
console.log(`${element.type}: ${element.name} - ${element.label}`);
});
const pageData = window.agentlet.forms.extract(document.body);
pageData.forms.forEach((form, index) => {
console.log(`Form ${index + 1}: ${form.elements.length} elements`);
const requiredFields = form.elements.filter((el) => el.required);
console.log(`Required fields: ${requiredFields.map((f) => f.name).join(', ')}`);
});
function fillFromExtraction(formData, values) {
for (const form of formData.forms) {
for (const element of form.elements) {
const value = values[element.name];
if (value === undefined || !element.interactable) continue;
const domElement = document.querySelector(element.selector);
if (!domElement) continue;
if (element.type === 'checkbox') {
domElement.checked = Boolean(value);
domElement.dispatchEvent(new Event('change', { bubbles: true }));
} else {
domElement.value = value;
domElement.dispatchEvent(new Event('input', { bubbles: true }));
}
}
}
}

For a higher-level API that already handles this dispatch logic and reports success and failure per field, use form filling instead of writing your own loop.

For large pages, limit the scope of extraction rather than scanning the whole document:

const container = document.getElementById('main-content');
const formData = window.agentlet.forms.extract(container);
// Or extract a specific form only
const targetForm = document.querySelector('form[data-form="registration"]');
if (targetForm) {
const formData = window.agentlet.forms.extract(targetForm);
}
try {
const formData = window.agentlet.forms.extract(element, options);
if (!formData || (!formData.forms.length && !formData.elements.length)) {
console.warn('No form elements found in the specified element');
}
} catch (error) {
console.error('Form extraction failed:', error);
// Fallback to basic form detection
const forms = element.querySelectorAll('form');
const inputs = element.querySelectorAll('input, select, textarea');
console.log(`Fallback found: ${forms.length} forms, ${inputs.length} inputs`);
}

For the clean, AI-oriented export instead of this full structure, see AI-ready forms.

Source: agentlet-core docs/form-extraction.md and src/types/public-api.d.ts at e3f78fa. The full extraction result documented here matches the current FormExtractionResult/FormElementInfo types; older examples in the source document that referenced selector lists with certainty scores, ARIA accessibility metadata, and free-text context were not carried over because they are not part of the current public API.