Handling frames in Selenium

What are frames/iframes?

HTML frames/iframes are used to divide the web page into multiple sections, so that they can load on the page as required. It could be a window or sub window within the page. We can show different types of elements within these frames. For ex, a frame could show static information while other can display a media output-video or audio etc.

If a page has more than one frames, they are defined under a <frameset> tag. An individual frame will be defined within <frame> tag and iframes within <iframe>. In some cases, we may see frames within frames. They are called nested frames.

When we right click on an element which is withina frame, we will see the options like this :

Take an example : https://the-internet.herokuapp.com/nested_frames

We have two frames named as frame-top and frame-bottom. When we expand the frame-top, we will see 3 more frames withing it :

How to handle them in Selenium:

In this example, the frames have name. We can use the switchTo() method to switch the driver context to the frame name/id we provide:

In case, we do not have a frame name or id, index index can be used. Or just finding the frame as a WebElement and supply it to switchTo().

        driver.get("https://the-internet.herokuapp.com/nested_frames");        
        //WebElement frameTop = driver.findElement(By.name("frame-top"));
        //driver.switchTo().frame(frameTop);
        driver.switchTo().frame("frame-top");
        List<WebElement> allFramesWithinTop = 
                    driver.findElements(By.xpath("//frame"));
        System.out.println("No.of inner frames :" 
                           + allFramesWithinTop.size()); // 3

Let's switch to inner frame - frame-left :

        driver.switchTo().frame("frame-left");
        System.out.println(driver.findElement(By.xpath("//body"))
                                 .getText()); 
//It will print LEFT

Now, if we want to access the next inner frame i.e. frame-right or frame-middle, we won't be able to do it from here. Because driver is within the frame-left context. If write the below code, we will get NoSuchElementException :

        driver.switchTo().frame("frame-left");
        System.out.println(driver.findElement(By.xpath("//body"))
                                 .getText());
        //switching directly to another inner frame :
        driver.switchTo().frame("frame-middle");
        System.out.println(driver.findElement(By.xpath("//body"))
                                  .getText());

Remember the "driver.switchTo().parentFrame()" in the screenshot above? We need to write that piece of line before switching to another inner frame within the same parent frame as below :

        driver.switchTo().frame("frame-left");
        System.out.println(driver.findElement(By.xpath("//body"))
              .getText());    // O/p : LEFT
        driver.switchTo().parentFrame(); //driver is at frame-top context
        driver.switchTo().frame("frame-middle");
        System.out.println(driver.findElement(By.xpath("//body"))
              .getText()); // O/p : MIDDLE

defaultContent() :

We can switch to the main context any moment using the defaultContent() method.

driver.switchTo().defaultContent(); //driver will be at the main
//current page context.
        driver.switchTo().frame("frame-left");
        System.out.println(driver.findElement(By.xpath("//body"))
                 .getText());//LEFT
        driver.switchTo().parentFrame();
        driver.switchTo().frame("frame-middle");
        System.out.println(driver.findElement(By.xpath("//body"))
                   .getText()); //MIDDLE
        driver.switchTo().defaultContent();
        List<WebElement> allParentFrames = driver.findElements(By.xpath("//frame"));
        System.out.println("No.of parent frames :" + allParentFrames.size());//2
//i.e. frame-top and frame-bottom