Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Call waitForSelector() on the Frame that contains the element—not on the top-level Page—and await the result:
const element = await frame.waitForSelector('button.submit', { visible: true });
This waits for a matching element in that frame. Puppeteer documents that the frame-level method works across navigations. Choose the frame first, set the wait conditions you need, and handle either the returned element handle or a timeout.
As an Amazon Associate I earn from qualifying purchases.
Find the frame that contains the element
A page can contain a main frame and child frames, including nested frames. The selector must be evaluated in the document where the target element lives. Use page.frames() to inspect the page’s frame tree, or childFrames() when traversing from a known frame. Select the frame by a property you can reliably identify, such as its URL.
const frame = page.frames().find(frame => frame.url().includes('/embedded-form'));
if (!frame) {
throw new Error('Embedded form frame not found');
}
A frame can navigate after you find it. The documented frame-level waitForSelector() works across navigations, so it is the appropriate wait when navigation may occur during the operation. Recheck your frame-selection logic if the URL changes before the lookup.
#1 Best Overall
Wait for the selector
Once you have the right frame, call frame.waitForSelector(selector, options) and await its promise. The selector can be an ordinary CSS selector or another selector form documented by Puppeteer.
const submit = await frame.waitForSelector('button[type="submit"]', {
visible: true,
timeout: 10_000,
});
if (!submit) {
throw new Error('Submit button was not found');
}
The default timeout documented for this method is 30,000 milliseconds. Set timeout to a different maximum wait in milliseconds; timeout: 0 disables the timeout. You can also pass an AbortSignal through the signal option to cancel the wait.
Rank #2
Choose the condition deliberately
visible: truewaits until a matching element is present and visible.hidden: truewaits until the matching element is hidden or absent. An absent selector can result innull.- With neither visibility option, the wait is for a matching element; it does not by itself establish that the element is visible or ready for a particular interaction.
A wait that does not find the requested selector before timing out throws an error. Handle that failure where you can report useful context or take a recovery action, rather than assuming every awaited wait returns an element.
Use the handle, then dispose of it
A successful wait resolves to an element handle. Use it for the operation you need, and dispose of it when finished so it does not remain alive unnecessarily.
const frame = page.frames().find(frame => frame.url().includes('/embedded-form'));
if (!frame) {
throw new Error('Embedded form frame not found');
}
const submit = await frame.waitForSelector('button[type="submit"]', {
visible: true,
timeout: 10_000,
});
if (!submit) {
throw new Error('Submit button was not found');
}
try {
await submit.click();
} finally {
await submit.dispose();
}
This example assumes the target frame is identifiable by that URL fragment and that clicking the button is the desired action. If a subsequent action fails, a successful selector wait does not automatically retry that action.
Choose between frame waits, locators, and element-handle waits
| API | Use it when | Important distinction |
|---|---|---|
frame.waitForSelector() |
You need to wait for a selector in a particular frame or obtain an element handle. | The frame API documents that the wait works across navigations. |
frame.locator() |
Your goal is an interaction such as clicking or filling. | Puppeteer’s guide recommends locators for selection and interaction; they wait for element presence and relevant action preconditions. |
ElementHandle.waitForSelector() |
You specifically need to wait relative to an existing element handle. | Its documented wait does not work across navigations or after the element is detached. Prefer the frame-level wait when those conditions matter. |
Use the API that matches the scope and outcome you need: a page, a particular frame, or a descendant of an existing element handle. For an interaction, a locator can avoid managing a returned handle yourself.
Rank #4
Troubleshoot a wait that fails
- The wait times out: confirm the selector is correct and that you chose the frame whose document contains the element. If the element is expected to appear later, check whether the timeout suits that wait.
- The frame was not found: inspect the frame tree and verify the property used to identify it. A URL-based match will not work if the frame URL does not contain the expected fragment.
- The wait resolves but the element is not usable: waiting for a match without
visible: truedoes not establish visibility. For interactions, consider a locator, which waits for relevant action preconditions. - The wait is cancelled: if you supplied a signal, check whether the operation that owns it aborted the wait; remove cancellation or use a signal with the intended lifetime.
- A later action fails:
waitForSelector()does not automatically retry the action. Reassess whether a locator better fits the interaction, and dispose of any element handle you no longer need. - A hidden wait returns
null: this can be the expected result when the selector is absent. Do not treat that result as a found element.
Or skip the browser setup
If the task is to capture a page screenshot rather than interact with an element inside a Puppeteer frame, ScreenshotNeo offers a screenshot API. It is not a replacement for frame-level DOM waits.
For example, this cURL request saves a screenshot of Stripe as WebP. See the ScreenshotNeo API documentation for request options.
Best Value
- Used Book in Good Condition
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent requests in Python and Node.js:
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}`);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; those cleanup steps can each be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. See ScreenshotNeo for details, or sign up free.
Frequently Asked Questions
Does frame.waitForSelector() work after navigation?
Puppeteer’s Frame API documents that it works across navigations.
Can a hidden wait succeed when the selector is absent?
Yes. With hidden waiting, an absent selector can resolve to null.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Quick Recap
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.




