ScreenshotNeo

BlogHow-to

How to Scroll to an Element With Puppeteer Locators

Use Puppeteer’s locator API to scroll an element by offset, understand automatic viewport scrolling, and troubleshoot common edge cases.

By the ScreenshotNeo team4 October 20266 min read

To explicitly scroll an element with Puppeteer’s locator API, call scroll() on its locator and pass vertical or horizontal offsets:

await page.locator('#results').scroll({ scrollTop: 400 });

This uses mouse wheel events. If your goal is only to click or otherwise act on an off-screen element, you usually do not need an explicit scroll: locator actions ensure the target is in the viewport by default. See Puppeteer’s page interactions guide and the scroll options reference.

1. Set up a runnable example

Install Puppeteer in a Node.js project:

npm install puppeteer

Save this as scroll.js and run it with node scroll.js. It opens a page, scrolls the element with ID target by 300 pixels vertically, and closes the browser.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setContent(`
      <div style="height: 1200px">Long page</div>
      <div id="target">Target element</div>
      <div style="height: 1200px">More content</div>
    `);

    await page.locator('#target').scroll({ scrollTop: 300 });
  } finally {
    await browser.close();
  }
})();

The selector can be CSS or Puppeteer selector syntax. For example, locators can select by text or accessibility role and name, and supported selector combinations can cross shadow roots. See Page.locator() for the current selector reference.

2. Choose the right kind of scrolling

Explicit offset scrolling

Use Locator.scroll() when you want to scroll the located element’s scrollable area by an amount:

await page.locator('.feed').scroll({ scrollTop: 500 });
await page.locator('.wide-table').scroll({ scrollLeft: 250 });
await page.locator('.map').scroll({ scrollTop: 100, scrollLeft: 80 });

scrollTop and scrollLeft are the vertical and horizontal scroll amounts. The options are documented in the LocatorScrollOptions interface. Use the scrollable container as the locator when the page has nested scrolling; targeting an item inside a nested panel is not the same as targeting the panel itself.

Automatic scrolling before locator actions

Locator actions check their preconditions and retry when the target is not ready. By default, they ensure that the target is in the viewport before acting. This is useful for an off-screen button you intend to click:

await page.locator('button.submit').click();

This automatic viewport behavior is not an offset-scrolling command. If you need a particular amount of scrolling, call scroll() explicitly. To disable automatic viewport ensuring for a locator, use the cloned locator returned by setEnsureElementIsInTheViewport(false):

const button = page
  .locator('button.submit')
  .setEnsureElementIsInTheViewport(false);

await button.click();

The setting defaults to true. Disabling it changes the viewport precondition for that locator; it does not perform or replace explicit scrolling. See setEnsureElementIsInTheViewport().

Explicitly bring an existing element handle into view

If your code already holds an ElementHandle and wants an into-view operation, the lower-level API is ElementHandle.scrollIntoView():

const handle = await page.$('#target');
if (!handle) throw new Error('Target not found');
await handle.scrollIntoView();

This differs from locator offset scrolling. The handle method brings the held element into view; Locator.scroll() sends wheel offsets. See ElementHandle.scrollIntoView().

3. Handle nested containers and dynamic pages

  1. Find the scroll container. If a panel, feed, or table has its own scrollbar, select that container and scroll it. Scrolling the document will not necessarily move the panel’s contents.
  2. Wait for the target to exist. Locator actions retry when their target is not ready, but your page may still need navigation or application-specific loading to finish before the intended element appears.
  3. Use offsets appropriate to the layout. A fixed offset moves by that amount; it does not promise to align an element with the top or center of the viewport.
  4. Account for page changes. Sticky headers, animations, lazy-loaded content, and layout shifts can change where the target ends up. If the task is to act on a target, a locator action’s viewport check is often simpler than guessing an offset.

When a scrollable region is hard to identify, inspect the page structure and select the element that owns the scrolling behavior. A locator’s selector identifies the node to scroll; it does not infer which ancestor you intended.

4. Troubleshoot common problems

Symptom Likely cause What to do
The page moves, but the panel does not The locator targets the document or a different element than the nested scroll container. Locate the panel that owns the scrollbar and call scroll() on it.
A click works without an explicit scroll Locator actions ensure the target is in the viewport by default. This is expected. Use explicit scroll() only when you need to move a scroll area by an offset.
The target is still not where expected An offset is a distance, not an alignment instruction; layout may also shift. Choose an appropriate offset, wait for the relevant layout to settle, or use an action that automatically ensures the target is in view.
The locator action keeps retrying or fails The element may not exist yet or may not meet action preconditions such as visibility or a stable bounding box. Check the selector and page state. Wait for the application’s content to load and confirm the target can be interacted with.
Disabling viewport ensuring did not scroll the element setEnsureElementIsInTheViewport(false) disables the automatic check; it is not a scroll command. Call scroll() for offset movement, or use scrollIntoView() when holding an element handle.
An ElementHandle is null The selector did not match an element at the time of lookup. Verify the selector and that the page has rendered the target before calling scrollIntoView().

5. Performance, reliability, and version notes

Prefer locators for normal page interaction: Puppeteer describes them as its recommended way to select and interact with page elements, and locator actions retry when an element is not ready. Avoid adding repeated, guessed scroll offsets when an action already brings its target into view. For long or dynamic pages, choose the correct scroll container and allow the application’s content to render before interacting.

The supplied documentation search identified the locator guide as version 25.12.0 and the scroll options reference as version 25.4.0. Check the documentation matching your installed Puppeteer version if a version-sensitive detail differs. The references do not provide benchmark numbers or a per-scroll cost; performance depends on the page and browser work involved.

6. Or skip the browser setup

If your goal is a screenshot rather than browser interaction, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; its documentation covers the API options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture, and each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. See the API documentation and sign up for 1,000 free screenshots a month, with no card.

7. FAQ

Does Locator.scroll() scroll the page or the selected element?

It uses mouse wheel events on the located target with the offsets you provide. For nested scrolling, target the intended scroll container.

Should I scroll before every click?

No. Locator actions ensure the target is in the viewport by default. Add explicit scrolling when you need offset movement or a specific scroll state.

Can I use text or accessibility selectors?

Yes. Puppeteer’s locator API accepts CSS and Puppeteer selector syntax, including text and accessibility role/name selectors. Check the current Page.locator() reference for supported forms.