scroll_to_element
Scrolls an element into view, automatically handling the nearest scrollable ancestor. Supports start, center, or end alignment to prepare elements for interaction or trigger lazy loading.
Instructions
Scroll an element into view. Automatically handles scrolling within the nearest scrollable ancestor (page or scrollable container). Essential for: making elements visible before interaction, triggering lazy-loaded content, testing scroll behavior. Position: start (top of viewport), center (middle), end (bottom). Default: start.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| selector | Yes | CSS selector, text selector, or test ID (e.g., 'testid:submit-btn', '#login-button', 'text=Load More') | |
| position | No | Where to align element in viewport: 'start' (top), 'center' (middle), 'end' (bottom). Default: 'start' |
Implementation Reference
- The execute() method that implements the scroll_to_element tool logic. It finds the element by selector, scrolls it into view using Playwright's scrollIntoViewIfNeeded() (default 'start' position) or scrollIntoView() with block:'center'/'end', and returns a success response with element details.
async execute(args: any, context: ToolContext): Promise<ToolResponse> { this.recordInteraction(); return this.safeExecute(context, async (page) => { const position = args.position || 'start'; // Use Playwright's built-in scrollIntoViewIfNeeded which handles scrollable containers const locator = await this.createScopedLocator(page, args.selector); // First check if element exists const count = await locator.count(); if (count === 0) { return createSuccessResponse([ `✗ Element not found: ${args.selector}`, ``, `💡 Try:`, ` • Use get_test_ids() to see available test IDs`, ` • Use inspect_dom() to explore page structure`, ` • Use find_by_text({ text: "..." }) to locate by content` ]); } // Use standard element selection with error on multiple matches const { element } = await this.selectPreferredLocator(locator, { errorOnMultiple: true, originalSelector: args.selector, }); // Scroll into view based on position if (position === 'center') { await element.evaluate((el) => { el.scrollIntoView({ block: 'center', inline: 'center' }); }); } else if (position === 'end') { await element.evaluate((el) => { el.scrollIntoView({ block: 'end', inline: 'end' }); }); } else { // 'start' or default await element.scrollIntoViewIfNeeded(); } // Get element tag and attributes for output const tagName = await element.evaluate((el) => el.tagName.toLowerCase()); const testId = await element.getAttribute('data-testid'); const id = await element.getAttribute('id'); const className = await element.getAttribute('class'); // Build element description let elementDesc = `<${tagName}`; if (testId) elementDesc += ` data-testid="${testId}"`; else if (id) elementDesc += ` id="${id}"`; else if (className) elementDesc += ` class="${className.split(' ').slice(0, 2).join(' ')}"`; elementDesc += '>'; const messages = [ `✓ Scrolled to element (position: ${position})`, elementDesc ]; // Add contextual suggestion - verify element is now visible messages.push(''); messages.push('💡 Common next step - Verify visibility:'); messages.push(` element_visibility({ selector: "${args.selector}" }) - Check if element is in viewport`); return createSuccessResponse(messages); }); } - The getMetadata() method defining the tool's name ('scroll_to_element'), description, and inputSchema. The schema requires a 'selector' string and has an optional 'position' enum with values 'start', 'center', 'end'.
static getMetadata(sessionConfig?: SessionConfig): ToolMetadata { return { name: "scroll_to_element", description: "Scroll an element into view. Automatically handles scrolling within the nearest scrollable ancestor (page or scrollable container). Essential for: making elements visible before interaction, triggering lazy-loaded content, testing scroll behavior. Position: start (top of viewport), center (middle), end (bottom). Default: start.", annotations: ANNOTATIONS.interaction, inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector, text selector, or test ID (e.g., 'testid:submit-btn', '#login-button', 'text=Load More')" }, position: { type: "string", description: "Where to align element in viewport: 'start' (top), 'center' (middle), 'end' (bottom). Default: 'start'", enum: ["start", "center", "end"] } }, required: ["selector"], }, }; - src/tools/browser/register.ts:6-104 (registration)Import and registration of ScrollToElementTool in the BROWSER_TOOL_CLASSES array (line 57), which makes the tool available in the MCP server.
import { ScrollToElementTool } from './navigation/scroll_to_element.js'; import { ScrollByTool } from './navigation/scroll_by.js'; // Lifecycle import { CloseTool } from './lifecycle/close.js'; import { SetColorSchemeTool } from './lifecycle/set_color_scheme.js'; // Interaction import { ClickTool } from './interaction/click.js'; import { FillTool } from './interaction/fill.js'; import { SelectTool } from './interaction/select.js'; import { HoverTool } from './interaction/hover.js'; import { UploadFileTool } from './interaction/upload_file.js'; import { DragTool } from './interaction/drag.js'; import { PressKeyTool } from './interaction/press_key.js'; // Content import { ScreenshotTool } from './content/screenshot.js'; import { GetTextTool } from './content/get_text.js'; import { GetHtmlTool } from './content/get_html.js'; // Inspection import { InspectDomTool } from './inspection/inspect_dom.js'; import { GetTestIdsTool } from './inspection/get_test_ids.js'; import { QuerySelectorTool } from './inspection/query_selector.js'; import { FindByTextTool } from './inspection/find_by_text.js'; import { CheckVisibilityTool } from './inspection/check_visibility.js'; import { CompareElementAlignmentTool } from './inspection/compare_element_alignment.js'; import { InspectAncestorsTool } from './inspection/inspect_ancestors.js'; import { ElementExistsTool } from './inspection/element_exists.js'; import { MeasureElementTool } from './inspection/measure_element.js'; import { GetComputedStylesTool } from './inspection/get_computed_styles.js'; // Evaluation import { EvaluateTool } from './evaluation/evaluate.js'; // Console import { GetConsoleLogsTool, ClearConsoleLogsTool } from './console/get_console_logs.js'; // Network import { ListNetworkRequestsTool } from './network/list_network_requests.js'; import { GetRequestDetailsTool } from './network/get_request_details.js'; // Waiting 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, ];