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 →Use handle.asElement() to check whether an existing Puppeteer JSHandle already refers to a DOM element. It returns an ElementHandle for an element, or null for any other value; it does not convert an ordinary JavaScript object into an element. If you need to obtain an element from page code, use page.evaluateHandle() and then check the result.
Check an existing handle with asElement()
JSHandle.asElement() narrows the handle at runtime. Its return type is ElementHandle<Node> | null, so check for null before calling element-specific methods such as click().
As an Amazon Associate I earn from qualifying purchases.
const element = handle.asElement();
if (element === null) {
throw new Error('This handle does not refer to a DOM element');
}
await element.click();
The method returns the same handle when the referenced value is already an element. It does not search for an element or turn a non-element value into one. See the Puppeteer asElement() API reference.
Obtain an element handle from page code
When you need to select or derive an element in the page, return it from evaluateHandle(). Unlike evaluate(), this keeps a reference to the returned page object. If the function returns an element, Puppeteer represents it as an ElementHandle.
#1 Best Overall
const handle = await page.evaluateHandle(() => document.querySelector('#submit'));
const element = handle.asElement();
if (!element) {
throw new Error('No element matched #submit');
}
await element.click();
This example accounts for both nullable steps: querySelector() returns null when no element matches, and asElement() returns null if the handle is not an element. The Page.evaluateHandle() reference documents retained handles and the TypeScript generic form.
Evaluate from an existing handle
If you already have a handle and want to evaluate code relative to the value it references, use handle.evaluateHandle(). Return the element you want to retain, then narrow the result with asElement() as needed.
Rank #2
const childHandle = await parentHandle.evaluateHandle(parent =>
parent.querySelector('.child')
);
const child = childHandle.asElement();
if (!child) {
throw new Error('No child element matched .child');
}
await child.click();
As with page-level evaluation, a selector that finds nothing yields a non-element result; handle that possibility before using element methods.
Choose between evaluate() and evaluateHandle()
| Need | Use | What you get |
|---|---|---|
| Read text, an attribute, a number, or other ordinary result | evaluate() |
The evaluated result, suitable for serialization |
| Keep a page object or DOM node for later operations | evaluateHandle() |
A retained JSHandle; a returned element is represented as an ElementHandle |
| Check whether a handle you already have refers to an element | asElement() |
The element handle or null |
Returning a DOM node through evaluate() does not preserve it as a usable element handle; the Puppeteer JavaScript execution guide demonstrates that returning document.body this way can serialize as {}. Use evaluateHandle() when later browser operations need the referenced object. See Puppeteer’s JavaScript execution guide.
TypeScript: use a generic only when the return is known
If the function is known to return an element, Puppeteer’s API documentation shows a TypeScript generic argument for ElementHandle:
const element = await page.evaluateHandle<ElementHandle>(() =>
document.querySelector('#submit')
);
A type annotation does not make a missing selector match at runtime. Since querySelector() may return null, retain a runtime check when the result is not guaranteed to exist. Verify the exact overload against the Puppeteer version installed in your project; the official documentation pages surfaced version labels from 25.1.0 through 25.12.0, rather than one identical release across every page.
Rank #4
Collect element-valued properties
When a handle refers to an object whose properties may themselves be DOM elements, call getProperties(), then test each property handle with asElement(). Keep only non-null results.
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 problemsconst properties = await bodyHandle.getProperties();
const elements = [];
for (const propertyHandle of properties.values()) {
const element = propertyHandle.asElement();
if (element) {
elements.push(element);
}
}
This is the documented pattern for obtaining element handles from property handles; see JSHandle.getProperties().
Best Value
Handle lifetime and cleanup
A JSHandle keeps its referenced page object from being garbage-collected while the handle is retained. Dispose of handles you no longer need:
await handle.dispose();
Puppeteer also automatically disposes handles when the associated frame navigates away or its execution context is destroyed. Do not rely on navigation for cleanup when a handle is simply no longer needed. Details are in the JSHandle API reference.
Troubleshoot common failures
asElement() returns null
- Cause: The handle refers to a non-element value, or the page expression returned
nullbecause a selector matched nothing. - Fix: Confirm what the evaluation returns, check selector spelling and page state, and guard the result before calling element methods. Use
evaluateHandle()to obtain the desired page-side reference;asElement()only checks its kind.
A DOM node returned from evaluate() is not usable as an element
- Cause:
evaluate()returns a serialized value, not a retained node reference. - Fix: Use
evaluateHandle()when later operations need the page object or element.
TypeScript reports a type mismatch
- Cause: The evaluated expression may return
nullor another value, while the annotation assumes an element. - Fix: Check for a matching element at runtime and confirm the generic overload in documentation or type declarations matching your installed Puppeteer release.
A handle is unusable after navigation
- Cause: Navigation or destruction of the parent execution context automatically disposes associated handles.
- Fix: Obtain a fresh handle after navigation, and explicitly dispose retained handles once finished with them.
Or skip the browser setup
If you need a screenshot rather than a Puppeteer element handle, ScreenshotNeo is a website screenshot API and MCP server for developers. A single GET request captures a URL; its cookie/consent cleanup can accept banners and remove 60+ known consent platforms, newsletter popups, and chat widgets before capture. Those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallExample cURL request (replace the URL with the page to capture):
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 documentation for setup and options. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for free.
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.




