Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use page.locator(...).filter(predicate) to narrow a useful set of candidate elements to the one that matches a condition, then perform the action on the refined locator. For example, Puppeteer’s documentation shows filtering buttons by exact textContent before clicking. The key detail is that the filter callback runs in the browser context, not as a normal Node.js callback with access to your outer variables.

Filter a locator by a condition

Start with a selector that identifies a meaningful candidate set, then use .filter() for the additional condition that distinguishes the intended element. Puppeteer’s documented example is:

await page
  .locator('button')
  .filter(button => button.textContent === 'My button')
  .click();

Here, button is the element being tested by the predicate. The CSS selector narrows the search to buttons; the predicate requires the button’s textContent to equal My button. Change the selector and condition to match the page you are automating.

A predicate expresses a condition; it does not itself prove that exactly one matching element exists. Choose a candidate selector and condition that make the target unambiguous for the page in question.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Pass Node.js values into the browser-context predicate

The function supplied to .filter() executes in the browser context. It cannot directly read a variable in your Node.js scope as an ordinary closure could. If the expected text comes from a Node variable, serialize the value into the function string:

const buttonName = 'My button';

await page
  .locator('button')
  .filter(`button => button.textContent === ${JSON.stringify(buttonName)}`)
  .click();

JSON.stringify() safely represents the string as a JavaScript string literal inside the predicate. This matters when the value contains quotes, backslashes, or other characters that would otherwise break the generated function. Do not assume a callback can capture arbitrary Node variables when Puppeteer evaluates it in the page.

Understand locator filtering and retries

Locator.filter(predicate) refines a locator by adding an expectation. It is not JavaScript’s Array.filter(): it does not immediately return an in-memory array of elements. Puppeteer retries the filter expectation when it does not match, and locator actions also retry while their required conditions are not met.

For a click(), Puppeteer documents automatic checks that include whether the element is in the viewport, visible and enabled, and whether its bounding box stays stable across two animation frames. These checks help with changing pages, but do not make every action share identical preconditions or guarantee that a predicate identifies the element you intended.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose between a predicate and selector syntax

Approach Use it when What to keep in mind
CSS selector A stable tag, class, attribute, or DOM relationship identifies the candidates directly. Puppeteer selector APIs accept CSS selectors.
.filter(predicate) You can locate candidates easily, but a custom condition—such as matching exact textContent—distinguishes the target. The predicate runs in the browser context and participates in locator expectation retries.
Text selector Visible text is a suitable way to describe the target. Puppeteer text selectors choose minimal elements containing the requested text and can search open shadow roots. Escape selector-sensitive characters as required by the selector syntax.
ARIA selector The computed accessible role and name describe the target reliably. Puppeteer computes these from the accessibility representation and resolves relationships such as labelledby. This can avoid dependence on particular DOM attributes or structure.
XPath The required DOM relationship is clearer in XPath. Puppeteer’s XPath selector uses the browser’s native Document.evaluate.
Shadow-DOM combinator The target is inside an open shadow root. >>> searches descendants at any depth; >>>> searches the immediate shadow root. These combinators have documented limits, including open-shadow-root and selector-depth constraints.

Prefer the most direct selector that expresses stable, user-facing meaning. Add a predicate when it communicates a condition the selector does not express clearly. No one strategy is universally most reliable: the page’s markup and the target’s meaning determine which is clearest.

Keep the locator on the correct page or frame

page.locator() creates a locator on a page. If the target belongs to a frame, use that frame’s locator entry point, frame.locator(), so the selector is evaluated in the correct context.

Troubleshoot common filtering problems

The predicate cannot find a Node.js variable

Cause: The predicate executes in the browser context and does not share a normal closure with Node.js. Fix: Embed values using safe serialization, as in the JSON.stringify() example, or use selector syntax that does not need the value passed into a predicate.

The click keeps waiting or retrying

Cause: The filter expectation may not yet match, or the element may not satisfy the action’s preconditions. The candidate text may differ from textContent, the page may still be changing, or the element may be hidden, disabled, outside the viewport, or moving. Fix: Check the candidate selector and predicate against the rendered page, confirm the target is in the expected page or frame, and verify that it becomes actionable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The predicate matches the wrong text-bearing element

Cause: A broad selector or text condition can include more than the intended target; textContent represents the element’s text content, not necessarily only the visible label a user perceives. Fix: Narrow the candidate set with a stable attribute or relationship, or choose a text or ARIA selector when it better expresses the target’s visible or accessible meaning.

A needed locator operation is unavailable

Cause: A locator may not expose every lower-level browser operation. Fix: Puppeteer identifies page.waitForSelector() and ElementHandle as alternatives. They are lower-level: waitForSelector() does not automatically retry an action after that action fails, and a returned element handle should be disposed of when no longer needed to avoid memory leaks.

A legacy selector prefix does not combine with another selector

Cause: Legacy forms such as text/My text, aria/My label, and xpath///h2 remain supported, but a legacy prefix runs one non-CSS selector at a time and cannot combine selectors. Fix: Use Puppeteer’s documented selector syntax for the selector types and combinations you need.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a page image or PDF rather than interact with its elements, ScreenshotNeo provides a screenshot API. This is a different task from filtering Puppeteer locators: the request captures a URL rather than selecting and clicking an element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request returns an image or PDF. Example cURL request:

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

See the ScreenshotNeo API documentation for request options. Cookie banners are accepted and removed along with supported consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots.

Sign up free for 1,000 screenshots a month—no card required.

Frequently Asked Questions

Does `.filter()` return an array of matching elements?

No. It refines a locator with an expectation; it is not `Array.filter()`.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can a `.filter()` predicate use a variable declared in Node.js?

Not through an ordinary captured closure. Serialize the value into the browser-context predicate, for example with `JSON.stringify()`.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.