How to Focus an Element Inside a Frame with Puppeteer
Focus an element in an iframe with Puppeteer by selecting its Frame, waiting for the target if needed, and calling frame.focus(selector).
To focus an element inside an iframe with Puppeteer, get the Frame that contains it and call await frame.focus(selector). For example:
await frame.focus('#target');
page.focus(selector) targets the main frame only. For a child frame, select that frame first. Puppeteer’s Frame.focus API focuses the first matching element and throws if there is no match.
1. Complete runnable example
This Node.js script opens a page, finds an iframe by its name attribute, waits for the input to appear inside it, and focuses the input.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
let targetFrame;
for (const frame of page.frames()) {
// The main frame has no iframe element, so skip it.
if (frame === page.mainFrame()) continue;
const element = await frame.frameElement().catch(() => null);
if (!element) continue;
const name = await element.evaluate(el => el.getAttribute('name'));
if (name === 'myframe') {
targetFrame = frame;
break;
}
}
if (!targetFrame) throw new Error('Target frame not found');
await targetFrame.waitForSelector('#target', { timeout: 10000 });
await targetFrame.focus('#target');
console.log('Focused #target in', targetFrame.url());
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
Replace https://example.com, myframe, and #target with the page URL, iframe identifier, and element selector for your case. The URL must actually contain the frame when this code runs.
2. Find the frame containing the element
A Puppeteer Frame represents a document frame. The page’s frame tree is available through page.frames(); page.mainFrame() identifies the top-level document, and each frame can expose its nested childFrames().
Match by frame name
The runnable example reads the name attribute from each frame’s iframe element. This works when the page gives the iframe a stable name. The frameElement() call can fail if the frame detaches while you inspect it, so the example skips that frame.
Match by URL
If the embedded document has a stable URL, compare frame.url() instead:
const targetFrame = page.frames().find(frame =>
frame.url().startsWith('https://widgets.example/checkout')
);
if (!targetFrame) throw new Error('Checkout frame not found');
await targetFrame.waitForSelector('[name="email"]');
await targetFrame.focus('[name="email"]');
Use a sufficiently specific URL condition if several frames share a host or route. For nested frames, inspect the frame tree or use parentFrame() and childFrames() to confirm which document contains the target.
3. Wait for rendering, then focus
If scripts create the input asynchronously, wait in the selected frame before focusing:
await frame.waitForSelector('#target', { timeout: 10000 });
await frame.focus('#target');
waitForSelector() waits for a matching element to appear in that frame and can work across navigations. If it does not appear before the timeout, it throws. Set a timeout that fits the page’s expected load behavior rather than waiting indefinitely.
Puppeteer locators are useful for interactions that have locator actions such as clicking or filling. The documented Locator API does not provide a focus action, so when the required action is specifically focus, use Frame.focus().
4. Selectors and focus behavior
CSS selectors work by default. Choose a selector that uniquely identifies the intended element in the selected frame. Puppeteer also documents selector syntax for text, accessibility attributes, XPath, and shadow DOM; see its selector and interaction guide for supported syntax.
frame.focus(selector) focuses the first match. It does not prove that the page’s application accepted the resulting input or that a visible focus indicator appeared. If you need to verify behavior, inspect the field or listen for the page’s relevant events after focusing.
For an explicit presence check, query the frame first. frame.$() returns the first matching element handle or null:
const handle = await frame.$('#target');
if (!handle) throw new Error('Target element is absent from this frame');
await frame.focus('#target');
await handle.dispose();
Use a try/finally or dispose handles after use in longer-running scripts. Often the direct waitForSelector() followed by focus() is simpler.
5. Main frame versus child frame
| Call | Where it searches | Use it when |
|---|---|---|
page.focus(selector) |
The main frame | The element belongs to the top-level document. |
frame.focus(selector) |
The selected frame | The element belongs to an iframe or another frame in the frame tree. |
Puppeteer documents page.focus(selector) as a shortcut for page.mainFrame().focus(selector). Switching to page.focus() will not search child frames.
6. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
No element found for selector or focus throws |
The selector does not match in this frame, or the element has not rendered. | Confirm the selected frame, check the selector with frame.$(), and wait with frame.waitForSelector(). |
| Frame not found | The matching rule is wrong, the iframe has not been added, or its URL/name differs from the assumption. | Inspect page.frames().map(f => f.url()) and check iframe attributes in the page. |
| Works on the main page but not in an iframe | page.focus() searches only the main frame. |
Find the child Frame and call frame.focus(). |
| Frame detached or navigation race | The iframe was removed or navigated while being selected or queried. | Re-read page.frames() after navigation and retry selection; wait for the frame’s target element after the new document loads. |
| Focus call succeeds but application does not proceed | Focus alone does not enter text, submit a form, or trigger every application-specific event. | Use the appropriate subsequent interaction, such as typing or clicking, and verify the application’s expected state. |
| Selector matches the wrong field | Multiple elements match and focus uses the first. | Use a more specific selector scoped to the intended frame and verify which element it resolves to. |
7. Performance, reliability, and version notes
Frame enumeration and attribute checks are generally small relative to page navigation and rendering, but avoid repeatedly scanning the entire frame tree in a tight loop. Select the frame once, wait for the target with a bounded timeout, and then focus it.
Frame selection is sensitive to page changes. A frame may appear later, navigate to a different URL, or detach. For reliable automation, identify it using a stable page-specific attribute or URL, reselect after navigation, and handle missing frames and timeouts explicitly.
Puppeteer’s API documentation is versioned, and the research for this guide surfaced multiple recent API versions. Check the documentation matching the Puppeteer version installed in your project when behavior or types differ.
8. Or skip the browser setup
If your goal is a screenshot of the page rather than interactive focus automation, ScreenshotNeo captures a URL through one API request. See the ScreenshotNeo API documentation for request 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}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for ScreenshotNeo to get 1,000 screenshots a month with no card.
9. FAQ
Can I focus an element in a cross-origin iframe?
Puppeteer operates on the frame object for the embedded document. Select the frame from Puppeteer’s frame tree and query within that frame rather than trying to access the iframe document from page JavaScript.
Does focusing an element type text into it?
No. Focus places the element in the active input position; typing is a separate interaction.
What happens if a selector matches several elements?
Frame.focus() focuses the first matching element. Narrow the selector if you need a particular match.


