Skip to main content
SeleniumDecoded

iFrames and Frames

Learn to switch between frames and iframes to interact with embedded content.

Selenium 3 & 4 Stable

iFrames (inline frames) embed external content within a page. Selenium can’t see elements inside frames until you explicitly switch to them.

Understanding Frames

Main Page
├── Header
├── Content
├── iframe#payment-frame
│ └── (Payment form elements live here)
└── Footer

To interact with elements inside the payment form, you must first switch to that iframe.

Switching to a Frame

Switch to Frame
Selenium 3 & 4 Stable
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
// Method 1: By frame element
WebElement frameElement = driver.findElement(By.id("payment-frame"));
driver.switchTo().frame(frameElement);
// Method 2: By frame ID or name attribute
driver.switchTo().frame("payment-frame"); // Works with id or name
// Method 3: By index (0-based)
driver.switchTo().frame(0); // First frame on page
// Now you can interact with elements inside the frame
WebElement cardInput = driver.findElement(By.id("card-number"));
cardInput.sendKeys("4111111111111111");
// With explicit wait (recommended)
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.id("payment-frame")));
driver.findElement(By.id("card-number")).sendKeys("4111111111111111");
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Method 1: By frame element
frame_element = driver.find_element(By.ID, "payment-frame")
driver.switch_to.frame(frame_element)
# Method 2: By frame ID or name attribute
driver.switch_to.frame("payment-frame") # Works with id or name
# Method 3: By index (0-based)
driver.switch_to.frame(0) # First frame on page
# Now you can interact with elements inside the frame
card_input = driver.find_element(By.ID, "card-number")
card_input.send_keys("4111111111111111")
# With explicit wait (recommended)
wait = WebDriverWait(driver, 10)
wait.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "payment-frame")))
driver.find_element(By.ID, "card-number").send_keys("4111111111111111")
const { until } = require('selenium-webdriver');
// Method 1: By frame element
const frameElement = await driver.findElement(By.id('payment-frame'));
await driver.switchTo().frame(frameElement);
// Method 2: By frame ID or name
await driver.switchTo().frame('payment-frame');
// Method 3: By index (0-based)
await driver.switchTo().frame(0); // First frame on page
// Now you can interact with elements inside the frame
const cardInput = await driver.findElement(By.id('card-number'));
await cardInput.sendKeys('4111111111111111');
// With explicit wait (recommended)
await driver.wait(until.ableToSwitchToFrame(By.id('payment-frame')), 10000);
await driver.findElement(By.id('card-number')).sendKeys('4111111111111111');
using OpenQA.Selenium.Support.UI;
// Method 1: By frame element
IWebElement frameElement = driver.FindElement(By.Id("payment-frame"));
driver.SwitchTo().Frame(frameElement);
// Method 2: By frame ID or name attribute
driver.SwitchTo().Frame("payment-frame"); // Works with id or name
// Method 3: By index (0-based)
driver.SwitchTo().Frame(0); // First frame on page
// Now you can interact with elements inside the frame
IWebElement cardInput = driver.FindElement(By.Id("card-number"));
cardInput.SendKeys("4111111111111111");
// With explicit wait (recommended)
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(By.Id("payment-frame")));
driver.FindElement(By.Id("card-number")).SendKeys("4111111111111111");

Switching Back to Main Content

Exit Frame
Selenium 3 & 4 Stable
// Currently inside a frame...
driver.findElement(By.id("submit")).click();
// Switch back to the main page (top-level document)
driver.switchTo().defaultContent();
// Now can access main page elements
WebElement mainHeader = driver.findElement(By.id("main-header"));
// Alternative: Switch to parent frame (one level up)
driver.switchTo().parentFrame();
# Currently inside a frame...
driver.find_element(By.ID, "submit").click()
# Switch back to the main page (top-level document)
driver.switch_to.default_content()
# Now can access main page elements
main_header = driver.find_element(By.ID, "main-header")
# Alternative: Switch to parent frame (one level up)
driver.switch_to.parent_frame()
// Currently inside a frame...
await driver.findElement(By.id('submit')).click();
// Switch back to the main page (top-level document)
await driver.switchTo().defaultContent();
// Now can access main page elements
const mainHeader = await driver.findElement(By.id('main-header'));
// Alternative: Switch to parent frame (one level up)
await driver.switchTo().parentFrame();
// Currently inside a frame...
driver.FindElement(By.Id("submit")).Click();
// Switch back to the main page (top-level document)
driver.SwitchTo().DefaultContent();
// Now can access main page elements
IWebElement mainHeader = driver.FindElement(By.Id("main-header"));
// Alternative: Switch to parent frame (one level up)
driver.SwitchTo().ParentFrame();

Nested Frames

When frames contain other frames:

Handling Nested Frames
Selenium 3 & 4 Medium
// Structure:
// Main Page
// └── outer-frame
// └── inner-frame
// └── target element
// Step 1: Switch to outer frame
driver.switchTo().frame("outer-frame");
// Step 2: Switch to inner frame (within outer)
driver.switchTo().frame("inner-frame");
// Step 3: Now interact with deeply nested elements
WebElement element = driver.findElement(By.id("nested-element"));
element.click();
// Going back out:
driver.switchTo().parentFrame(); // Now in outer-frame
driver.switchTo().parentFrame(); // Now in main page
// Or jump straight to main:
driver.switchTo().defaultContent();
# Structure:
# Main Page
# └── outer-frame
# └── inner-frame
# └── target element
# Step 1: Switch to outer frame
driver.switch_to.frame("outer-frame")
# Step 2: Switch to inner frame (within outer)
driver.switch_to.frame("inner-frame")
# Step 3: Now interact with deeply nested elements
element = driver.find_element(By.ID, "nested-element")
element.click()
# Going back out:
driver.switch_to.parent_frame() # Now in outer-frame
driver.switch_to.parent_frame() # Now in main page
# Or jump straight to main:
driver.switch_to.default_content()
// Structure:
// Main Page
// └── outer-frame
// └── inner-frame
// └── target element
// Step 1: Switch to outer frame
await driver.switchTo().frame('outer-frame');
// Step 2: Switch to inner frame (within outer)
await driver.switchTo().frame('inner-frame');
// Step 3: Now interact with deeply nested elements
const element = await driver.findElement(By.id('nested-element'));
await element.click();
// Going back out:
await driver.switchTo().parentFrame(); // Now in outer-frame
await driver.switchTo().parentFrame(); // Now in main page
// Or jump straight to main:
await driver.switchTo().defaultContent();
// Structure:
// Main Page
// └── outer-frame
// └── inner-frame
// └── target element
// Step 1: Switch to outer frame
driver.SwitchTo().Frame("outer-frame");
// Step 2: Switch to inner frame (within outer)
driver.SwitchTo().Frame("inner-frame");
// Step 3: Now interact with deeply nested elements
IWebElement element = driver.FindElement(By.Id("nested-element"));
element.Click();
// Going back out:
driver.SwitchTo().ParentFrame(); // Now in outer-frame
driver.SwitchTo().ParentFrame(); // Now in main page
// Or jump straight to main:
driver.SwitchTo().DefaultContent();

Frame Helper Methods

Reusable Frame Utilities
Selenium 3 & 4 Stable
public class FrameHelper {
private WebDriver driver;
private WebDriverWait wait;
public FrameHelper(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public void switchToFrame(By locator) {
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(locator));
}
public void switchToFrame(String nameOrId) {
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(nameOrId));
}
public void switchToFrame(int index) {
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(index));
}
public void switchToMain() {
driver.switchTo().defaultContent();
}
public void switchToParent() {
driver.switchTo().parentFrame();
}
// Execute action in frame, then return to main
public <T> T executeInFrame(By frameLocator, java.util.function.Supplier<T> action) {
switchToFrame(frameLocator);
try {
return action.get();
} finally {
switchToMain();
}
}
}
// Usage
FrameHelper frames = new FrameHelper(driver);
frames.switchToFrame(By.id("payment-frame"));
// interact...
frames.switchToMain();
// Or with auto-return
String cardNumber = frames.executeInFrame(
By.id("payment-frame"),
() -> driver.findElement(By.id("card")).getText()
);
class FrameHelper:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def switch_to_frame(self, locator):
"""Switch to frame by locator tuple, name/id string, or index"""
if isinstance(locator, tuple):
self.wait.until(EC.frame_to_be_available_and_switch_to_it(locator))
elif isinstance(locator, str):
self.wait.until(EC.frame_to_be_available_and_switch_to_it(locator))
elif isinstance(locator, int):
self.wait.until(EC.frame_to_be_available_and_switch_to_it(locator))
def switch_to_main(self):
self.driver.switch_to.default_content()
def switch_to_parent(self):
self.driver.switch_to.parent_frame()
def execute_in_frame(self, locator, action):
"""Execute action in frame, then return to main"""
self.switch_to_frame(locator)
try:
return action()
finally:
self.switch_to_main()
# Usage
frames = FrameHelper(driver)
frames.switch_to_frame((By.ID, "payment-frame"))
# interact...
frames.switch_to_main()
# Or with auto-return
card_number = frames.execute_in_frame(
(By.ID, "payment-frame"),
lambda: driver.find_element(By.ID, "card").text
)
class FrameHelper {
constructor(driver) {
this.driver = driver;
}
async switchToFrame(locator) {
await this.driver.wait(until.ableToSwitchToFrame(locator), 10000);
}
async switchToMain() {
await this.driver.switchTo().defaultContent();
}
async switchToParent() {
await this.driver.switchTo().parentFrame();
}
async executeInFrame(locator, action) {
await this.switchToFrame(locator);
try {
return await action();
} finally {
await this.switchToMain();
}
}
}
// Usage
const frames = new FrameHelper(driver);
await frames.switchToFrame(By.id('payment-frame'));
// interact...
await frames.switchToMain();
// Or with auto-return
const cardNumber = await frames.executeInFrame(
By.id('payment-frame'),
async () => {
const card = await driver.findElement(By.id('card'));
return await card.getText();
}
);
public class FrameHelper
{
private IWebDriver driver;
private WebDriverWait wait;
public FrameHelper(IWebDriver driver)
{
this.driver = driver;
this.wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
}
public void SwitchToFrame(By locator)
{
wait.Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(locator));
}
public void SwitchToFrame(string nameOrId)
{
wait.Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(nameOrId));
}
public void SwitchToMain()
{
driver.SwitchTo().DefaultContent();
}
public void SwitchToParent()
{
driver.SwitchTo().ParentFrame();
}
public T ExecuteInFrame<T>(By frameLocator, Func<T> action)
{
SwitchToFrame(frameLocator);
try
{
return action();
}
finally
{
SwitchToMain();
}
}
}
// Usage
var frames = new FrameHelper(driver);
frames.SwitchToFrame(By.Id("payment-frame"));
// interact...
frames.SwitchToMain();

Common Frame Scenarios

Payment Forms (Stripe, PayPal)

Stripe Payment Frame
Selenium 3 & 4 Medium
// Stripe embeds card input in iframes
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Card number frame
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(
By.xpath("//iframe[contains(@name, 'privateStripeFrame')]")
));
driver.findElement(By.name("cardnumber")).sendKeys("4242424242424242");
driver.switchTo().defaultContent();
// Expiry frame (different iframe)
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(
By.xpath("//iframe[contains(@title, 'expiry')]")
));
driver.findElement(By.name("exp-date")).sendKeys("1225");
driver.switchTo().defaultContent();
# Stripe embeds card input in iframes
wait = WebDriverWait(driver, 10)
# Card number frame
wait.until(EC.frame_to_be_available_and_switch_to_it(
(By.XPATH, "//iframe[contains(@name, 'privateStripeFrame')]")
))
driver.find_element(By.NAME, "cardnumber").send_keys("4242424242424242")
driver.switch_to.default_content()
# Expiry frame (different iframe)
wait.until(EC.frame_to_be_available_and_switch_to_it(
(By.XPATH, "//iframe[contains(@title, 'expiry')]")
))
driver.find_element(By.NAME, "exp-date").send_keys("1225")
driver.switch_to.default_content()
// Stripe embeds card input in iframes
// Card number frame
await driver.wait(until.ableToSwitchToFrame(
By.xpath("//iframe[contains(@name, 'privateStripeFrame')]")
), 10000);
await driver.findElement(By.name('cardnumber')).sendKeys('4242424242424242');
await driver.switchTo().defaultContent();
// Expiry frame
await driver.wait(until.ableToSwitchToFrame(
By.xpath("//iframe[contains(@title, 'expiry')]")
), 10000);
await driver.findElement(By.name('exp-date')).sendKeys('1225');
await driver.switchTo().defaultContent();
// Stripe embeds card input in iframes
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
// Card number frame
wait.Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(
By.XPath("//iframe[contains(@name, 'privateStripeFrame')]")
));
driver.FindElement(By.Name("cardnumber")).SendKeys("4242424242424242");
driver.SwitchTo().DefaultContent();
// Expiry frame (different iframe)
wait.Until(ExpectedConditions.FrameToBeAvailableAndSwitchToIt(
By.XPath("//iframe[contains(@title, 'expiry')]")
));
driver.FindElement(By.Name("exp-date")).SendKeys("1225");
driver.SwitchTo().DefaultContent();

Best Practices

  1. Always wait for frame before switching to it
  2. Return to default content after frame interactions
  3. Use helper classes for cleaner code
  4. Identify frames by ID or name over index when possible
  5. Handle nested frames carefully with parent navigation

Next Steps