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)└── FooterTo 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 elementWebElement frameElement = driver.findElement(By.id("payment-frame"));driver.switchTo().frame(frameElement);
// Method 2: By frame ID or name attributedriver.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 frameWebElement 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 WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC
# Method 1: By frame elementframe_element = driver.find_element(By.ID, "payment-frame")driver.switch_to.frame(frame_element)
# Method 2: By frame ID or name attributedriver.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 framecard_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 elementconst frameElement = await driver.findElement(By.id('payment-frame'));await driver.switchTo().frame(frameElement);
// Method 2: By frame ID or nameawait 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 frameconst 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 elementIWebElement frameElement = driver.FindElement(By.Id("payment-frame"));driver.SwitchTo().Frame(frameElement);
// Method 2: By frame ID or name attributedriver.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 frameIWebElement 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 elementsWebElement 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 elementsmain_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 elementsconst 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 elementsIWebElement 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 framedriver.switchTo().frame("outer-frame");
// Step 2: Switch to inner frame (within outer)driver.switchTo().frame("inner-frame");
// Step 3: Now interact with deeply nested elementsWebElement element = driver.findElement(By.id("nested-element"));element.click();
// Going back out:driver.switchTo().parentFrame(); // Now in outer-framedriver.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 framedriver.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 elementselement = driver.find_element(By.ID, "nested-element")element.click()
# Going back out:driver.switch_to.parent_frame() # Now in outer-framedriver.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 frameawait 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 elementsconst element = await driver.findElement(By.id('nested-element'));await element.click();
// Going back out:await driver.switchTo().parentFrame(); // Now in outer-frameawait 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 framedriver.SwitchTo().Frame("outer-frame");
// Step 2: Switch to inner frame (within outer)driver.SwitchTo().Frame("inner-frame");
// Step 3: Now interact with deeply nested elementsIWebElement element = driver.FindElement(By.Id("nested-element"));element.Click();
// Going back out:driver.SwitchTo().ParentFrame(); // Now in outer-framedriver.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(); } }}
// UsageFrameHelper frames = new FrameHelper(driver);frames.switchToFrame(By.id("payment-frame"));// interact...frames.switchToMain();
// Or with auto-returnString 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()
# Usageframes = FrameHelper(driver)frames.switch_to_frame((By.ID, "payment-frame"))# interact...frames.switch_to_main()
# Or with auto-returncard_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(); } }}
// Usageconst frames = new FrameHelper(driver);await frames.switchToFrame(By.id('payment-frame'));// interact...await frames.switchToMain();
// Or with auto-returnconst 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(); } }}
// Usagevar 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 iframesWebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Card number framewait.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 iframeswait = WebDriverWait(driver, 10)
# Card number framewait.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 frameawait driver.wait(until.ableToSwitchToFrame( By.xpath("//iframe[contains(@name, 'privateStripeFrame')]")), 10000);await driver.findElement(By.name('cardnumber')).sendKeys('4242424242424242');await driver.switchTo().defaultContent();
// Expiry frameawait 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 iframesWebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
// Card number framewait.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
- Always wait for frame before switching to it
- Return to default content after frame interactions
- Use helper classes for cleaner code
- Identify frames by ID or name over index when possible
- Handle nested frames carefully with parent navigation
Next Steps
- Windows and Tabs - Switch between windows
- Shadow DOM - Handle shadow DOM elements
- Explicit Waits - Wait for frame availability
- JavaScript Executor - Execute scripts in frames
- Debugging Tips - Troubleshoot frame issues