wait_for_element
Wait for an element to become visible, hidden, attached, or detached. Replaces unreliable sleep() calls for dynamic content. Supports CSS selectors and testid shortcuts.
Instructions
Wait for an element to reach a specific state (visible, hidden, attached, detached). Better than sleep() for waiting on dynamic content. Returns duration and current element status. Supports testid shortcuts (e.g., 'testid:submit-button').
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| selector | Yes | CSS selector, text selector, or testid shorthand (e.g., 'testid:submit-button', '#loading-spinner') | |
| state | No | State to wait for: 'visible' (default), 'hidden', 'attached', 'detached' | |
| timeout | No | Maximum time to wait in milliseconds (default: 10000) |
Implementation Reference
- The execute() method that implements the wait_for_element tool logic. Uses Playwright's locator.waitFor() to wait for an element to reach a specified state (visible, hidden, attached, detached). Returns duration and current element status, or a timeout error.
async execute(args: WaitForElementArgs, context: ToolContext): Promise<ToolResponse> { return this.safeExecute(context, async (page) => { const { selector, state = 'visible', timeout = 10000 } = args; const locator = await this.createScopedLocator(page, selector); const startTime = Date.now(); try { await locator.waitFor({ state, timeout }); const duration = ((Date.now() - startTime) / 1000).toFixed(1); // Check current state const isVisible = await locator.isVisible().catch(() => false); const count = await locator.count(); const exists = count > 0; const statusLines = [ `✓ Element ${state} after ${duration}s`, `Now: ${isVisible ? '✓ visible' : '✗ hidden'}, ${exists ? '✓ exists' : '✗ not found'}` ]; return { content: [{ type: 'text', text: statusLines.join('\n') }], isError: false, }; } catch (error) { const duration = ((Date.now() - startTime) / 1000).toFixed(1); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text', text: `✗ Timeout after ${duration}s waiting for element to be ${state}\nError: ${errorMessage}` }], isError: true, }; } }); } } - The WaitForElementArgs interface and inputSchema definition defining the tool's input: selector (required), state (optional, enum of visible/hidden/attached/detached, default 'visible'), and timeout (optional, default 10000ms).
export interface WaitForElementArgs { selector: string; state?: 'visible' | 'hidden' | 'attached' | 'detached'; timeout?: number; } export class WaitForElementTool extends BrowserToolBase { static getMetadata(sessionConfig?: SessionConfig): ToolMetadata { return { name: "wait_for_element", description: "Wait for an element to reach a specific state (visible, hidden, attached, detached). Better than sleep() for waiting on dynamic content. Returns duration and current element status. Supports testid shortcuts (e.g., 'testid:submit-button').", annotations: ANNOTATIONS.readOnly, inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector, text selector, or testid shorthand (e.g., 'testid:submit-button', '#loading-spinner')" }, state: { type: "string", description: "State to wait for: 'visible' (default), 'hidden', 'attached', 'detached'", enum: ["visible", "hidden", "attached", "detached"] }, timeout: { type: "number", description: "Maximum time to wait in milliseconds (default: 10000)" } }, required: ["selector"], }, }; - The WaitForElementTool class definition extending BrowserToolBase, implementing the ToolHandler interface with getMetadata() and execute() methods.
export class WaitForElementTool extends BrowserToolBase { static getMetadata(sessionConfig?: SessionConfig): ToolMetadata { return { name: "wait_for_element", description: "Wait for an element to reach a specific state (visible, hidden, attached, detached). Better than sleep() for waiting on dynamic content. Returns duration and current element status. Supports testid shortcuts (e.g., 'testid:submit-button').", annotations: ANNOTATIONS.readOnly, inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector, text selector, or testid shorthand (e.g., 'testid:submit-button', '#loading-spinner')" }, state: { type: "string", description: "State to wait for: 'visible' (default), 'hidden', 'attached', 'detached'", enum: ["visible", "hidden", "attached", "detached"] }, timeout: { type: "number", description: "Maximum time to wait in milliseconds (default: 10000)" } }, required: ["selector"], }, }; } - src/tools/browser/register.ts:50-104 (registration)Import and registration of WaitForElementTool in the BROWSER_TOOL_CLASSES array at lines 50 (import) and 102 (registration in the 'Waiting' section).
import { WaitForElementTool } from './waiting/wait_for_element.js'; import { WaitForNetworkIdleTool } from './waiting/wait_for_network_idle.js'; export const BROWSER_TOOL_CLASSES: ToolClass[] = [ // Navigation (5) NavigateTool, GoHistoryTool, ScrollToElementTool, ScrollByTool, // Lifecycle (2) CloseTool, SetColorSchemeTool, // Interaction (7) ClickTool, FillTool, SelectTool, HoverTool, UploadFileTool, DragTool, PressKeyTool, // Content (3) ScreenshotTool, GetTextTool, GetHtmlTool, // Inspection (10) InspectDomTool, GetTestIdsTool, QuerySelectorTool, FindByTextTool, CheckVisibilityTool, CompareElementAlignmentTool, InspectAncestorsTool, ElementExistsTool, MeasureElementTool, GetComputedStylesTool, // Evaluation (1) EvaluateTool, // Console (2) GetConsoleLogsTool, ClearConsoleLogsTool, // Network (2) ListNetworkRequestsTool, GetRequestDetailsTool, // Waiting (2) WaitForElementTool, WaitForNetworkIdleTool, ];