ScreenshotNeo

BlogHow-to

How to Hover Over an Element Inside an Iframe with Puppeteer

Use the iframe’s Puppeteer Frame to find and hover its element. Learn frame selection, runnable examples, common fixes, and when to use a screenshot API instead.

By the ScreenshotNeo team4 October 20265 min read

To hover over an element inside an iframe with Puppeteer, get the Frame for that iframe, then locate and hover the element within that frame:

await frame.locator('.target').hover();

page.hover() searches the main frame, so it does not directly target a child iframe’s document. Puppeteer’s locator API is the recommended interaction style; frame.hover(selector) is also available. See the Frame API and page interactions guide.

1. Find the iframe and hover its element

Use the iframe’s stable URL or name to identify its frame. This complete Node.js example launches Chromium, finds a child frame, hovers a target, and closes the browser:

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' });

    const frame = page.frames().find(candidate =>
      candidate.url().includes('/embedded-widget')
    );
    if (!frame) throw new Error('Target iframe was not found');

    await frame.locator('.target').hover();
  } finally {
    await browser.close();
  }
})();

Replace the example URL fragment and selector with values for your page. A frame’s URL may change after navigation, so use an identifying attribute or other page-specific condition that remains reliable in your application.

Select by iframe element

When the iframe is identified by a CSS selector, wait for it and resolve its content frame:

const iframeHandle = await page.waitForSelector('iframe#widget');
const frame = await iframeHandle.contentFrame();
if (!frame) throw new Error('Iframe has no available content frame');
await frame.locator('button.submit').hover();

This is useful when multiple frames have similar URLs. The iframe element belongs to the parent document; the target selector belongs to the frame document.

Nested iframes

For nested frames, find the parent frame first, then locate its child iframe and resolve that iframe’s content frame. Repeat for each nesting level. A selector in the top-level page cannot directly address a document several frames deep.

2. Choose a hover API

Method Behavior Use it when
frame.locator(selector).hover() Uses a locator scoped to the frame. Locator actions wait for the element and check action readiness. Preferred for new code and dynamic pages.
frame.hover(selector) Hovers the center of the first matching element in that frame. You want the direct Frame API.
page.hover(selector) Shortcut for hovering in the main frame. The target is in the top-level document.

Example using the direct Frame method:

await frame.hover('.target');

Frame selectors use Puppeteer’s documented selector syntax. Prefer a selector unique within the iframe; if the page has duplicate matches, narrow it to the intended element.

3. Understand hover readiness and timing

A hover can fail even after the iframe is found if the target is absent, hidden, moving, or not yet ready. Locator actions wait for their action preconditions, including visibility, viewport placement, and a stable bounding box over consecutive animation frames. This is generally more robust than adding an arbitrary sleep.

If the site reveals the target only after asynchronous work, wait for the relevant condition in the same frame:

await frame.waitForSelector('.target', { visible: true });
await frame.locator('.target').hover();

Use a fixed delay only when the application has a known delay that cannot be observed through a selector or other condition. For hover menus, wait for the resulting menu or state after hovering if subsequent steps depend on it.

4. Common errors and fixes

Symptom Likely cause Fix
Element not found The selector was searched in the main frame, or it does not match in the selected frame. Use frame.locator() or frame.hover(); inspect the frame URL and confirm the selector in that document.
No matching frame The iframe has not loaded, its URL differs from the assumed fragment, or the wrong frame was selected. Wait for the iframe element, then resolve its content frame; log page.frames().map(f => f.url()) while debugging.
Timeout during hover The element is not visible, is outside the viewport, keeps moving, or never appears. Check visibility and selector correctness; wait for the application’s actual ready state and avoid racing animations.
Frame detached or navigation error The iframe navigated or was removed while the interaction was being prepared. After navigation or replacement, reacquire the iframe’s Frame and retry when the new document is ready.
Hover succeeds but no menu appears The application may require a different target, pointer position, or additional application state. Confirm the selector and inspect the page’s hover behavior; wait for the expected menu selector to appear.

Puppeteer reports frame attachment, navigation, and detachment as lifecycle events. Treat a frame reference as tied to the current document lifecycle and reacquire it after replacement.

5. Reliability, performance, and version notes

  • Prefer conditions to sleeps. Waiting for the frame and target state avoids wasting time on fast pages and racing slow ones.
  • Keep frame selection specific. A broad URL substring can match the wrong frame when a page embeds similar widgets.
  • Handle lifecycle changes. If the frame navigates, resolve it again before interacting with its new content.
  • Use your installed version’s docs. Puppeteer API documentation can vary across versions; check the version in your project lockfile and open the matching documentation.
  • Cost. Local Puppeteer does not charge per hover, but browser execution consumes machine time and resources. Reuse a browser for related work and close it in a finally block.

6. Or skip the browser setup

If you need a screenshot of a page rather than a scripted hover interaction, ScreenshotNeo provides a website screenshot API and MCP server. A single request can capture a URL as an image or PDF; see the API documentation.

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

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for free and get 1,000 screenshots a month with no card.

7. FAQ

Can Puppeteer hover an element in a cross-origin iframe?

Puppeteer’s frame APIs operate on the frame document, so use the iframe’s Frame and locate the element there. The page’s browser security rules still apply to page JavaScript that tries to inspect cross-origin content directly.

Does hovering trigger JavaScript mouse events?

The hover action moves Puppeteer’s pointer to the element. Page handlers responding to pointer or mouse movement can then run; wait for the application result you need before continuing.

Should I use a locator or frame.hover()?

Prefer frame.locator(selector).hover() for locator waiting and readiness checks. The direct frame.hover(selector) method is available for straightforward cases.

Why does the same selector work on the page but fail in the iframe?

Selectors are evaluated in a document context. The top-level page and each iframe have separate documents, so use the Frame that contains the target.