A tester spent an afternoon on a payment form. The card field was visibly on the screen. Every selector she tried returned nothing. She checked the spelling twenty times.
The field was inside an iframe, and that is one of the most confusing failures in Selenium precisely because everything looks correct.
An iframe is a whole separate document embedded in the page. It has its own DOM. When you search for an element, Selenium searches the document it is currently focused on, and by default that is the main page. Elements inside a frame are invisible to that search, even though they are visible to you.
⚠️ This is the classic Selenium confusion.
The element is on the screen. Your selector is correct.
findElementstill throwsNoSuchElementException.Nothing about the error hints at frames, which is why people lose hours. Whenever a selector fails for something you can plainly see, check the Elements tab for an
<iframe>wrapping it.
Switching in:
// by locating the frame element, the clearest option
const frame = await driver.findElement(By.css('iframe[title="Card details"]'));
await driver.switchTo().frame(frame);
// now you are inside; ordinary selectors work
await driver.findElement(By.css('[data-testid="card-number"]')).sendKeys('4111111111111111');
And back out, which people forget:
await driver.switchTo().defaultContent();
defaultContent() returns to the top-level page. If you skip it, every selector afterwards still searches inside the frame and fails, and by then the failure looks unrelated to the frame you entered three lines ago.
For a frame inside a frame, switch in twice. To go up one level only, switchTo().parentFrame().
Three things worth knowing.
Where iframes actually appear. Payment fields (Stripe and similar embed them deliberately, so card numbers never touch your page), embedded videos, maps, chat widgets, adverts, and rich text editors. If you test an e-commerce checkout, you will meet this.
Switching contexts invalidates element references. An element you found in the main page becomes stale once you switch into a frame. Find elements after switching, not before.
Content from another domain is restricted. You can interact with fields inside a payment iframe, but the browser's security rules limit what you can read across origins. If something behaves oddly inside a third-party frame, that is usually why, and it is not a Selenium bug.
Remember this: an iframe is its own document. Switch in, do the work, and always switch back.
Reference: Working with iframes.