Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Call await frame.evaluateHandle(() => expression) on the Puppeteer Frame whose JavaScript context you need. It returns a handle to the in-page result, so you can keep and use an object reference from that frame. Use frame.evaluate() when you only need a serializable value in Node.js.
Get a handle from the intended frame
First identify the frame, then call its evaluateHandle() method. This example finds a frame by part of its URL; replace that predicate with a stable criterion for your page.
As an Amazon Associate I earn from qualifying purchases.
const frame = page.frames().find(candidate => candidate.url().includes('/embedded/'));
if (!frame) throw new Error('Target frame not found');
const handle = await frame.evaluateHandle(() => window.someObject);
try {
// Use the handle with Puppeteer handle APIs or as an argument to an evaluation.
const summary = await handle.evaluate(object => object.name);
console.log(summary);
} finally {
await handle.dispose();
}
Frame.evaluateHandle(pageFunction, ...args) behaves like Page.evaluateHandle(), but evaluates in that frame’s JavaScript context. See the Puppeteer Frame.evaluateHandle() API for the method signature and version-specific details.
Choose the right frame
A page can contain nested frames, each with its own JavaScript context. page.evaluateHandle() evaluates in the main frame; it does not retrieve an object from a child frame. Inspect the frame tree with page.mainFrame() and frame.childFrames(), or use page.frames() to examine the page’s current frames.
#1 Best Overall
const mainFrame = page.mainFrame();
const children = mainFrame.childFrames();
for (const child of children) {
console.log(child.url());
}
Choose a frame using a criterion that is meaningful for your page, such as its URL or its relationship in the frame tree. A URL substring is convenient but may match more than one frame or change over time; check that the selected frame is the one that owns the target object. The Puppeteer Frame reference documents frame-tree inspection and frame-scoped selector methods.
Choose between a value and a handle
| Need | Use | What you get |
|---|---|---|
| A serializable result for Node.js | frame.evaluate(() => expression) |
The evaluated value returned to Node.js. |
| A reference to an in-page object | frame.evaluateHandle(() => expression) |
A JSHandle, or an ElementHandle when the result is a DOM element. |
| Just to select or inspect an element | frame.$(), frame.$eval(), or frame.$$eval() |
A selector-oriented operation scoped to that frame. |
Use a handle when subsequent work needs the original in-page object rather than a serialized copy. For example, DOM nodes are not ordinary values that serialize usefully into Node.js; returning one by handle preserves it as a browser-side reference.
Rank #2
Common handle patterns
Get the frame’s document
const documentHandle = await frame.evaluateHandle(() => document);
try {
const title = await documentHandle.evaluate(doc => doc.title);
console.log(title);
} finally {
await documentHandle.dispose();
}
Get a DOM element
If you specifically return a DOM element, Puppeteer gives you an ElementHandle:
const buttonHandle = await frame.evaluateHandle(() =>
document.querySelector('button')
);
try {
if (!buttonHandle) throw new Error('Button not found');
console.log(await buttonHandle.evaluate(button => button.textContent));
} finally {
await buttonHandle?.dispose();
}
For a simple selector task, frame.$('button') may be clearer than evaluating document.querySelector(). Use frame.$eval() to run a function on a matched element or frame.$$eval() to work with all matches in one call.
Pass Node.js values into the frame explicitly
The callback runs in the browser’s frame context. It cannot close over variables or helper functions from the Node.js scope. Pass data through evaluateHandle()’s additional arguments instead:
const propertyName = 'title';
const valueHandle = await frame.evaluateHandle(name => window[name], propertyName);
try {
console.log(await valueHandle.jsonValue());
} finally {
await valueHandle.dispose();
}
Arguments must be values Puppeteer can transfer into the page context. Do not write a callback that references a Node.js variable directly unless it is passed as an argument.
Rank #4
Dispose of handles and account for frame lifecycle
A JSHandle keeps its referenced object from being garbage-collected while the handle remains active. Call dispose() when finished; try/finally makes cleanup happen even when later work throws. Puppeteer also disposes handles when their associated frame navigates away or their parent execution context is destroyed, so a handle is not a durable reference across navigation.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Acquire and use a handle while the target frame is still in the relevant lifecycle. If navigation or context destruction occurs between acquisition and use, reacquire the frame and handle after the page reaches the state you need.
Best Value
- Used Book in Good Condition
Troubleshooting
- The result comes from the wrong document. The call was made on the main frame or another frame. Find the target in the frame tree and call
evaluateHandle()on thatFrame. - No frame matches the lookup. The frame may not yet exist, its URL may differ from the assumed pattern, or the predicate may be too restrictive. Inspect
page.frames()and the frame URLs at the point you perform the lookup; choose a stable identifying condition. - The callback reports that a Node.js name is undefined. The callback cannot access caller scope. Pass the value as an argument:
frame.evaluateHandle((value) => ..., value). - A returned DOM node is missing or unusable in Node.js. A DOM node is an in-page object, not a plain serialized object. Return it with
evaluateHandle(), or use the frame’s selector methods if you only need to query or act on it. - A handle fails after navigation. Navigation or destruction of the frame’s execution context invalidates references. Wait for the intended document, locate the current frame again, and create a fresh handle.
- Handles accumulate during repeated work. Dispose each handle after its last use, including error paths. Prefer
evaluate()or a selector method when a persistent object reference is unnecessary.
Version note
The examples use Puppeteer’s documented Frame.evaluateHandle() API. Puppeteer documentation pages are versioned, and signatures or types can change; consult the API reference matching the version installed in your project. The JavaScript execution guide is labeled “Next,” so treat it as potentially prerelease guidance rather than a guarantee for every installed version: Puppeteer JavaScript execution guide.
Or skip the browser setup
If your goal is a clean screenshot rather than a Puppeteer object reference, ScreenshotNeo offers a one-request screenshot API and an MCP server for AI agents. Its cleanup accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers.
cURL example (replace the URL with the page you need):
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemscurl -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. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Learn more at ScreenshotNeo.
Sign up free for 1,000 screenshots a month, no card required.
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.




