ScreenshotNeo

BlogHow-to

How to Get an Iframe Element from a Puppeteer Frame

Use Puppeteer's frameElement() to get the iframe DOM element that hosts a Frame. Learn how to inspect it, find frames by name, handle navigation, and troubleshoot common issues.

By the ScreenshotNeo team4 October 20267 min read

Call await frame.frameElement() to get the ElementHandle for the DOM element that hosts a Puppeteer Frame. Use that handle to inspect or interact with the outer <iframe> element. To query content inside the embedded document, use the Frame itself, for example frame.$().

const iframeElement = await frame.frameElement();
const name = await iframeElement.evaluate(el => el.getAttribute('name'));
console.log(name);

See the Puppeteer Frame.frameElement() API reference. The API examples below use Puppeteer’s documented frame and element-handle methods.

1. Understand Frame versus iframe ElementHandle

A Frame represents a browsing context. It provides methods to evaluate JavaScript and find elements in that frame’s document. An ElementHandle represents a particular DOM element in a page, such as the <iframe> node in the parent document.

Object or method What it represents or does
Frame The embedded browsing context and its document.
frame.frameElement() Gets the host iframe element handle from a child frame.
frame.$('selector') Finds an element inside that frame’s document.
iframeElement.contentFrame() Gets the associated Frame from an iframe element handle.

Keep the direction clear: Frame.frameElement() goes from a frame to its host element; ElementHandle.contentFrame() goes from the iframe element to its frame. A main frame normally has no hosting iframe element, because it is the page’s top-level browsing context.

2. Get the host element from an existing Frame

If you already have the target Frame, call frameElement() and await the promise:

const iframeElement = await frame.frameElement();

const id = await iframeElement.evaluate(el => el.id);
const title = await iframeElement.evaluate(el => el.getAttribute('title'));
console.log({ id, title });

The returned handle lets you inspect iframe attributes or perform element operations on the host node. It does not turn the frame’s document into a DOM node in the parent document.

3. Find a frame by its iframe name

When you have a page but not the target frame, iterate through its frames, inspect each host element, and keep the frame whose iframe has the desired name. The main frame has no iframe host in the usual parent-child sense, so skip it.

const frames = page.frames();
let targetFrame = null;

for (const frame of frames) {
  if (frame === page.mainFrame()) continue;

  try {
    const iframeElement = await frame.frameElement();
    const name = await iframeElement.evaluate(el => el.getAttribute('name'));

    if (name === 'myframe') {
      targetFrame = frame;
      break;
    }
  } catch (error) {
    // A frame may detach or navigate while it is being inspected.
    console.warn('Could not inspect a frame host:', error.message);
  }
}

if (targetFrame) {
  const text = await targetFrame.$eval('.selector', element => element.textContent);
  console.log(text);
} else {
  console.error('Frame with name "myframe" not found.');
}

Matching by name is useful when the page has multiple embedded documents. If the site exposes a stable iframe id, inspect that attribute instead. After identifying the host, use the matching Frame to query inside it.

4. Convert an iframe element handle back to a Frame

If you started with a selector for the iframe node, get its handle from the parent page and call contentFrame():

const iframeElement = await page.$('iframe#myframe');

if (!iframeElement) {
  throw new Error('iframe#myframe was not found');
}

const frame = await iframeElement.contentFrame();
const heading = await frame.$eval('h1', element => element.textContent);
console.log(heading);

This is the reverse direction of frame.frameElement(). The documented iframe-element signature returns a Promise<Frame>. Treat the result as an associated frame, then use frame methods to work with its document.

5. Run a complete example

This runnable CommonJS example launches Chromium, opens a page, finds a child frame by the host iframe’s name, reads text inside that frame, and closes the browser even if an error occurs. Install Puppeteer with npm install puppeteer; Puppeteer downloads a compatible browser as part of its standard installation.

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 = null;
    for (const frame of page.frames()) {
      if (frame === page.mainFrame()) continue;

      try {
        const host = await frame.frameElement();
        const name = await host.evaluate(el => el.getAttribute('name'));
        if (name === 'myframe') {
          targetFrame = frame;
          break;
        }
      } catch (error) {
        console.warn('Skipping a frame that changed during inspection:', error.message);
      }
    }

    if (!targetFrame) {
      throw new Error('Child frame named "myframe" was not found');
    }

    const host = await targetFrame.frameElement();
    const attributes = await host.evaluate(el => ({
      id: el.id,
      name: el.getAttribute('name'),
      src: el.getAttribute('src'),
    }));
    console.log('iframe host:', attributes);

    const text = await targetFrame.$eval('body', body => body.innerText);
    console.log('frame body:', text);
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

Replace the example URL and myframe with a page and iframe name you control. The example URL itself may not contain an iframe with that name.

6. Handle detached frames and stale handles

Frames and element handles are tied to page and document lifecycle. A frame can detach while the page changes, and an element handle can become invalid when its associated frame navigates away or its execution context is destroyed. Do not keep an iframe handle indefinitely and assume it remains usable.

  • Find the frame and obtain its host handle close to the operation that needs it.
  • If evaluation fails because the frame detached or navigated, refresh the frame list and locate the current frame again.
  • Check for a missing selector before calling methods on its handle.
  • Catch lifecycle errors around inspection when pages update dynamically.

For the current frame tree, use page.frames(); the page also exposes page.mainFrame(), and a frame exposes childFrames(). Do not assume a frame object or a handle survives a navigation.

7. Troubleshooting

Symptom Likely cause Fix
frame.frameElement is not a function The value is not a Puppeteer Frame, or a different object was passed. Check how the variable is assigned. Values from page.frames() are frames; an iframe selector returns an element handle.
The code finds no matching iframe The iframe name differs, the target is the main frame, or the frame has not attached yet. Inspect current page.frames(), verify the host’s name or id, and wait for the page state that adds the iframe before searching.
Cannot read properties of null or a missing-element error A selector such as page.$('iframe#myframe') found no element. Check the selector and page state; test the handle for null before calling contentFrame().
Evaluation fails with a detached or destroyed context error The frame detached, navigated, or its execution context was destroyed during the operation. Reacquire the frame and element handle after the navigation or DOM update, and retry only when the page is in the expected state.
Attributes are present but content queries return nothing The host element and the embedded document are being confused, or the selector is absent in that frame. Use the host handle to inspect iframe attributes; use frame.$() or frame.$eval() for content inside it.

8. Performance, reliability, and cost

For a page with many frames, reading each host attribute requires an asynchronous handle operation. Narrow the search when the page provides a stable selector or known frame relationship, and stop iterating once you find the target. Avoid repeating a full frame scan for every query. Reacquire handles after navigation instead of retrying operations against stale ones.

Puppeteer runs a browser process, so account for browser startup, page loading, and cleanup in your own runtime and hosting costs. Reuse a browser process across jobs where your application’s isolation requirements allow it, create the page for each job, and always close pages or browsers when finished. The relevant costs depend on your runtime, browser hosting, and workload; the API method itself does not set a service price.

9. Or skip the browser setup

If the task is to capture a website rather than inspect Puppeteer’s iframe object model, ScreenshotNeo is a website screenshot API and MCP server. Its API can return a screenshot or PDF without managing a browser in your code.

See the ScreenshotNeo API documentation for request options. This cURL request saves a screenshot:

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

Equivalent Python example:

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)

Equivalent Node.js example:

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 Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));
  • Cookie and consent banners, newsletter popups, and chat widgets from more than 60 known platforms are removed before capture; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers report the page verdict and billing status.
  • An 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 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

10. FAQ

Does frameElement() return the iframe’s contents?

No. It returns a handle to the host iframe element. Use the Frame to query the embedded document.

Can I call frameElement() on the main frame?

The main frame is the top-level page context and has no host iframe element in the normal parent-child relationship. Use it directly for the top-level document.

What should I retain between operations?

Retain a frame or handle only while its page context remains valid. After navigation or context destruction, locate the current frame and obtain a fresh handle.