ScreenshotNeo

BlogHow-to

How to Select an Option in a Puppeteer Frame

Select a dropdown value inside an iframe with Puppeteer by finding its Frame and calling frame.select() with the option’s value.

By the ScreenshotNeo team4 October 20268 min read

To select a dropdown option inside an iframe, find the Puppeteer Frame that contains the <select>, then call frame.select(selector, value). Pass the option’s value attribute, not its visible label. For a multiple select, pass each desired value as an additional argument.

const frame = page.frames().find(frame => frame.url().includes('/preferences'));
if (!frame) throw new Error('Preferences frame not found');

const selectedValues = await frame.select('select#colors', 'blue');
console.log(selectedValues); // ['blue'] when that value was selected

page.select() is a shortcut for selecting in the main frame. It does not search child frames. The API details are in the Puppeteer Frame.select() reference and Frame reference.

1. Install Puppeteer and open a page

The example below is a complete Node.js script. It launches Chromium, navigates to a page, finds a frame by part of its URL, selects an option, and closes the browser. Install Puppeteer in your project first:

npm install puppeteer

Save as select-frame-option.mjs and replace the example URL and frame path with the target page’s values:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto('https://example.com/settings', {
    waitUntil: 'domcontentloaded',
  });

  const frame = page.frames().find(candidate =>
    candidate.url().includes('/preferences')
  );
  if (!frame) {
    throw new Error('Preferences iframe was not found');
  }

  const values = await frame.select('select#colors', 'blue');
  console.log('Selected values:', values);
} finally {
  await browser.close();
}

Use a frame identity that is stable for your page. A URL fragment is convenient when the iframe URL is distinctive; a frame name or a known iframe element can be more reliable when URLs are dynamic. Puppeteer exposes the current frame tree through page.mainFrame(), frame.childFrames(), and page.frames().

2. Find the frame that owns the dropdown

Each frame has its own document context. A selector evaluated in the main frame cannot reach into a child iframe, and evaluating inside a parent frame does not automatically search nested child frames. First identify the right frame; then resolve the select’s CSS selector inside it.

Inspect all frames

for (const frame of page.frames()) {
  console.log({
    name: frame.name(),
    url: frame.url(),
    detached: frame.detached,
  });
}

This is useful when the page has multiple or nested iframes. page.frames() includes the main frame as well as attached child frames. Avoid depending on array position: frame order can change as the page loads or replaces frames.

Match a frame by name or URL

const target = page.frames().find(frame => frame.name() === 'preferences');
// Or:
const targetByUrl = page.frames().find(frame =>
  new URL(frame.url()).pathname === '/embedded/preferences'
);

if (!target && !targetByUrl) {
  throw new Error('Target frame not found');
}

Some frames have an empty name, and applications may generate changing URLs. In those cases, inspect the iframe element’s attributes and choose an identity your page keeps stable. If the frame is nested, search all frames with page.frames() or walk children with childFrames().

Use the main frame when that is where the select lives

const frame = page.mainFrame();
const values = await frame.select('select#colors', 'blue');
// Equivalent shortcut:
const sameValues = await page.select('select#colors', 'blue');

Use page.select() only for controls in the main document. For a child iframe, call select() on the corresponding Frame.

3. Select one or more values

The method signature is frame.select(selector, ...values). The selector must identify a <select> element, and values correspond to option value attributes. The method operates on the first matching select in that frame and resolves to an array of values it successfully selected.

Single-select dropdown

// HTML: <option value="blue">Blue</option>
const selected = await frame.select('select#colors', 'blue');
console.log(selected); // ['blue'] if selected

Even if the visible text is “Blue,” pass blue, the option value. If the markup omits a value attribute, inspect how the browser represents that option’s value before choosing the argument.

Multiple-select dropdown

const selected = await frame.select(
  'select#colors',
  'red',
  'green',
  'blue'
);
console.log(selected); // Values successfully selected

For a <select multiple>, Puppeteer considers all supplied values. For a normal single-select, it considers only the first supplied value. Pass only the values needed by the next step, and inspect the returned array if selection success matters.

Verify the selected value

const values = await frame.select('select#colors', 'blue');
if (!values.includes('blue')) {
  throw new Error(`Could not select blue; selected: ${values.join(', ')}`);
}

const currentValue = await frame.$eval(
  'select#colors',
  select => select.value
);
console.log('Current value:', currentValue);

Selection and application behavior are separate: changing the select does not guarantee that the page has finished handling its resulting events or navigation. If a selection triggers a dependent update, wait for the resulting page state before continuing.

4. Wait for a dynamically loaded control

Frame.select() throws if no matching select exists when called. If the iframe or control is added asynchronously, wait for the frame and element before selecting. Puppeteer’s locator API is often the simplest option when automatic readiness waiting is useful.

const frame = page.frames().find(candidate =>
  candidate.url().includes('/preferences')
);
if (!frame) throw new Error('Preferences iframe not found');

await frame.locator('select#colors').fill('blue');

frame.locator(selector).fill(value) supports select elements. Locators wait for an element to be present and ready for the action, as described in Puppeteer’s interaction guide and Locator.fill() reference. Use Frame.select() when you want its direct selection API, especially to provide multiple values at once.

If a locator is not appropriate, wait explicitly:

await frame.waitForSelector('select#colors', { visible: true });
const values = await frame.select('select#colors', 'blue');

Waiting for the frame itself may also be necessary if it has not attached yet. Poll or wait for the page’s known iframe condition before searching page.frames(); do not assume a frame exists immediately after the main page’s initial navigation.

5. Troubleshooting

Symptom Likely cause Fix
Frame.select() throws that no select matched The CSS selector is wrong, the control has not loaded, or the call used the wrong frame. Log each frame’s name and URL, confirm the select exists in that frame, wait for it if it is dynamic, and narrow the selector.
page.select() cannot find a select in an iframe page.select() targets the main frame. Find the child Frame and call frame.select().
The call completes but the desired option is not selected The argument was the visible label instead of the option value, or the value is not present in that select. Inspect the option values and pass the exact value. Check the returned array and the select’s current value.
The wrong dropdown changes The selector matches multiple select elements; Frame.select() operates on the first match. Use a unique ID, a scoped selector, or another stable CSS selector.
Frame lookup returns no match The iframe has not attached, has navigated, or its URL/name differs from the assumed identity. Inspect page.frames() after navigation; wait for the iframe condition and match a stable name or URL component.
The next action runs before dependent content updates The select changed, but the application’s asynchronous handler has not finished. Wait for a concrete outcome such as a result element, changed URL, or updated field before continuing.
The frame disappears between lookup and selection The page replaced or detached the iframe during navigation or rerendering. Reacquire the current frame after the transition, then wait for the control again.

6. Performance, reliability, and cost

Selecting an option is usually a small browser action; most delays come from launching Chromium, loading the page, waiting for the iframe, or waiting for the site’s response. Reuse a browser process across related jobs when your application’s isolation requirements allow it, and avoid fixed sleeps where a selector or page-state condition can express readiness more precisely.

For reliability, use a stable frame identity, a specific select selector, and an explicit post-selection condition when the page reacts asynchronously. Handle frame detachment and navigation as lifecycle events: reacquire the frame after a replacement instead of retaining a stale reference. Keep timeouts bounded and report which frame and selector failed so the cause is diagnosable.

Cost depends on where Chromium runs and how often you launch it; this Puppeteer workflow has no ScreenshotNeo charge. If you only need a page image rather than interactive selection or browser automation, ScreenshotNeo provides a separate screenshot API described below.

7. Or skip the browser setup

If the goal is a screenshot of the page after you have finished automating it, ScreenshotNeo can return an image with one GET request. Its API does not perform the Puppeteer dropdown interaction shown above; use Puppeteer when you need to select the option or drive the page first.

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

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

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/settings"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/settings',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
  • Cookie banners, popups, and chat widgets are removed before the shot.
  • Bot checks, blank pages, and failed loads are never billed.
  • An MCP server lets AI agents use screenshot and page information tools.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

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

8. FAQ

Can I select an option by its visible text?

Frame.select() takes option values. If you only know the visible text, inspect the select’s options and map that text to its value before calling the method.

Does this work with nested iframes?

Yes. Find the frame at the nesting level that directly contains the select. Searching page.frames() considers the page’s current frames, including nested ones.

Does selecting an option submit the form?

Not by itself. The page may respond to its change event, but submitting a form or waiting for application-specific effects is a separate step.

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

Use frame.select() for direct value selection and multiple-select values. Use frame.locator(...).fill(...) when locator readiness checks fit the interaction.