“Conquering Hidden Elements: Your Third Step into Advanced Playwright Automation”
- SunAi Murugan
- Aug 2
- 3 min read
Building on your element interaction skills from our previous journey, it’s time to tackle one of web automation’s trickiest challenges: working with hidden and nested content. As you advance in your Playwright expertise, you’ll inevitably encounter iframes and complex document structures that can make even seasoned automation engineers scratch their heads. These nested contexts are everywhere in modern web applications — from embedded payment forms to third-party widgets — and knowing how to handle them separates beginners from automation pros.
Understanding Frames vs IFrames
Frames represent an outdated method for creating site layouts using multiple documents from the same domain. Though you’ll rarely encounter them in modern applications, they still appear in legacy systems built before HTML5 standards took hold. On the other hand, iframes remain a cornerstone of contemporary web development, enabling developers to seamlessly embed content from entirely different domains within their applications.
Whether you’re automating tests for a cutting-edge React application or maintaining scripts for legacy enterprise systems, both Selenium and Playwright provide robust capabilities for interacting with these structures. The key lies in understanding how to identify and work with these different contexts effectively.

The IFrame
Let’s start with a common scenario. When you need to interact with elements inside iframes — such as clicking a button embedded within an iframe — you’ll encounter HTML structure like this:
html
<div id=”modal”>
<iframe id=”buttonframe” name=”myframe” src=”https://seleniumhq.github.io">
<button>Click here</button>
</iframe>
</div>
Your first instinct might be to write straightforward code:
Selenium:
await driver.findElement(By.css(‘button’)).click();
Playwright:
await page.locator(‘button’).click();
But here’s the frustrating reality: you write a perfectly good script to click that button, but instead of working, you get hit with a dreaded “no such element” error. Everything looks right in your code, so what’s going wrong?
The culprit is the iframe itself. When Selenium runs your script, it can only “see” elements that exist in the main document. If your target button lives inside an iframe, Selenium has no idea it exists — it’s like trying to find something in a house when you’re only looking through the front window.
This is where the fundamental difference between Selenium and Playwright becomes apparent.
Selenium’s Approach: Frame Switching
Just like switching between browser windows, you need to tell Selenium WebDriver to switch its focus to the iframe first. Think of it as walking through the front door and then entering the specific room where your button is located.
Method 1: Using Element Reference
// Store the web element
const iframe = driver.findElement(By.css(‘#modal > iframe’));
// Switch to the frame
await driver.switchTo().frame(iframe);
// Now we can click the button
await driver.findElement(By.css(‘button’)).click();
Method 2: Using Name or ID
// Using the ID
await driver.switchTo().frame(“buttonframe”);
// Or using the name instead
await driver.switchTo().frame(“myframe”);
// Now we can click the button
await driver.findElement(By.css(“button”)).click();
Method 3: Using Index
// Switches to the second frame (index 1)
await driver.switchTo().frame(1);
Playwright’s Smarter Approach: Direct Frame References
Unlike Selenium’s “switching” approach, Playwright takes a more intuitive path — instead of jumping in and out of frames, you simply grab a reference to the frame you need and work with it directly.
Method 1: Element Handle to Frame
// Get the iframe element handle
const iframeElementHandle = await page.locator(‘#modal > iframe’).elementHandle();
// Get the frame from the iframe element
const frame = await iframeElementHandle.contentFrame();
// Now interact with elements inside the iframe
await frame.locator(‘button’).click();
Method 2: Direct Frame Access by Name/ID
// Switch to iframe by ID or name
const frame = page.frame({ name: ‘buttonframe’ }) || page.frame({ name: ‘myframe’ });
// Always check if frame exists
if (frame) {
await frame.locator(‘button’).click();
} else {
throw new Error(‘Frame not found’);
}
Method 3: Using Frame Index
const allFrames = page.frames();
const frame = allFrames[1]; // Index 1 = second iframe
if (frame) {
await frame.locator(‘button’).click();
} else {
throw new Error(‘Frame at index 1 not found’);
}
Key Differences and Best Practices
Playwright Advantages:
No Context Switching: You get a reference to the frame and act within it directly
Best Practices:
Always check if the frame is null to avoid runtime errors
Use descriptive names or IDs when possible rather than relying on indexes
Remember that frame indexes can change if the page structure updates
Test your frame interactions across different browsers and page states
Take away
While both tools handle iframes effectively, Playwright’s direct reference model feels more intuitive than Selenium’s context switching, eliminating the mental overhead of tracking frame states. Mastering these iframe techniques — regardless of your chosen tool — will save you countless debugging hours when those frustrating “element not found” errors strike.
“Everything you’ll ever need to know is within you; the secrets of the universe are imprinted on the cells of your body.”


