Let's say you want to build a website that shows directions of nearby restaurants, and you would like to integrate Google Maps to help customers looking for directions. Google Maps is an external web resource that is getting embedded in your website. Similarly, if a user would want to embed a YouTube video into a web page, they can mention the link to the video in the HTML of the page, and the video gets added. Yes, this is where the use of iframes in Web Development comes in, to embed objects such as external web pages or various documents, images, videos into a single web page. These embedded objects can be maps, videos, images, or any external HTML document, and how to locate and automate these iframes in Selenium is the agenda for this post.
In this tutorial, we will use our ToolsQA demo website to learn different ways to handle iframes using Selenium WebDriver, by covering the details under the following topics:
- What are iframes?
- What is the difference between a frame and an iframe?
- How to automate iframes using Selenium WebDriver?
- How to switch to an iframe By Index in Selenium?
- And, how to switch to an iframe By Name or Id in Selenium?
- How to switch to an iframe By WebElement in Selenium?
- How to handle nested iframes in Selenium WebDriver?
- Also, how to switch the context back to the parent iframe from the child iframe?
- How to switch the context back to the main web page from the child iframe?
- How to handle dynamic iframes using Selenium WebDriver?
What are iframes?
An iframe is a space on the web page that embeds different kinds of media like images, documents, videos inside the main web page. The media can be internal images or documents related to the website or external sources from other websites. The full form of the iframe is Inline Frame and is defined using <iframe>
tag in HTML.
You can insert an iframe element by using the <iframe>
tag in an HTML document. The 'src' attribute of the tag specifies the URL of the media we have to embed in the*** iframe.*** The syntax of using the iframe in the HTML document is:
<iframe src="URL"></iframe>
Though websites widely use iframes, they bring in security risks. Moreover, websites can become vulnerable to cross-site attacks as the pages generally host on an external domain. Thus, an iframe requires a developer to trust the content he/she has embedded in the iframe.
To view an example of iframe on a web page, you can visit the ToolsQA demo site, where documents are embedded using iframe tag, as shown in the screenshot below -
Before moving further in the details of iframe, let's understand one more term, "frame". It is very widely used in web development and generally confuses with the iframe. However, both of these are very much different and serve different purposes.
What is the difference between a frame and an iframe?
In web development, there are two terminologies: frame and iframe, which are used widely and sounds similar. But both of these have different usage and purpose.
Frames are the HTML tags that divide the browser's window into multiple parts, where each part can load a separate HTML document. Each frame is assigned its webpage. The <frame>
tag denotes a frame, and all the frames are present in a <frameset>
tag in HTML. A frameset is defined as a collection of frames in a browser window and allows you to split the screen into different pages, as shown in the image below:
The basic syntax of defining and using frames in an HTML document looks as follows:
<frameset rows="25%,25%",50%,50%>
<frame name="frame1" src="frame_1.html" />
<frame name="frame2" src="frame_2.html" />
<frame name="frame2" src="frame_3.html" />
<frame name="frame2" src="frame_4.html" />
</frameset>
In contrast, iframes, as already explained, are inline frames used to embed content from internal/external websites and denoted by the <iframe>
tag. Unlike frames, these do not need a frameset tag and can be placed anywhere on the web page in a region inside the <body>
tag as shown in the image below:
Generally, the content present in iframes are embedded from third-party websites that are hosted externally outside your domain, so they are considered less secure than frames. If the content in iframes is not third-party but internal to the website, security issues come under observation very rarely.
How to automate iframes using Selenium WebDriver?
As we know, for automating a web page using Selenium WebDriver, the first task is to locate the web elements on the page. But, Selenium WebDriver can't access and locate the web elements inside the iframes in Selenium directly. Selenium can only access the elements in a specific context, and the context of the main web page and the embedded iframe are different. Selenium explicitly needs to switch the context so as it can access the elements inside the iframe.
Let's understand the same with the help of the following example on the web page "https://demoqa.com/frames", where we will try to print the text -"This is a sample page " as highlighted in the below screenshot:
Now, if we try to access the <h1>
element(Shown by Marker 2) by using its id "sampleHeading", as shown in the below code snippet:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//navigate to url
driver.get("https://demoqa.com/frames");
//Locate the frame1 heading
WebElement frame1Heading= driver.findElement(By.id("sampleHeading"));
//Finding the text of the frame1 heading
String frame1Text=frame1Heading.getText();
//Print the heading
System.out.println("Text of the frame1 heading is:"+frame1Text);
//closing the driver
driver.close();
}
}
The output of the above code will render "NoSuchElementException " as shown in the below screenshot, because the Selenium WebDriver is unable to find the web element with the mentioned id in the current context.
Now, as is evident from the above screenshot, Selenium can't access the elements which are wrapped in the iframe(Shown by Marker 1 in the above image). Selenium first needs to switch the context to the *iframe *to access all the web elements inside the iframe. Selenium WebDriver provides three ways to switch the focus to a specified iframe:
- using Index of the iframe.
- using Name or Id of the iframe
- And, using the Web Element object of the iframe.
Selenium WebDriver provides the switchTo().frame() method to switch the execution context to the identified iframe. Let's understand how to use this method with all the ways as mentioned above of switching the context to specified iframe:
How to switch to an iframe By Index in Selenium?
If there are multiple iframes on a web page, we can switch to each one of them using the index of the iframe in Selenium. Provided they have static positions on the web page. The index is zero-based. It means that if you have 2 frames on a page, the index will start from 0.
To switch to a particular* iframe* on a web page, Selenium WebDriver uses the following syntax:
driver.switchTo().frame(int index);
where the index is the index of the iframe to which Selenium needs to switch the context.
For instance, if you check the ToolsQA demo site, it has two frames, so to get the text of a heading inside the first iframe, we can switch to it using its index =0 as shown in the below code snippet:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//navigate to url
driver.get("https://demoqa.com/frames");
//Switch to Frame using Index
driver.switchTo().frame(0);
//Identifying the heading in webelement
WebElement frame1Heading= driver.findElement(By.id("sampleHeading"));
//Finding the text of the heading
String frame1Text=frame1Heading.getText();
//Print the heading text
System.out.println(frame1Text);
//closing the driver
driver.close();
}
}
The output of the code will print the text "This is a sample page " as shown below:
How to switch to an iframe By Name or Id in Selenium?
Selenium also provides the overloaded method to switch the context to the specified iframe in Selenium, which accepts the name or id of the iframe. It can be represented syntactically as follows:
driver.switchTo().frame(String name);
or
driver.switchTo().frame(String id);
Where the id or name of the iframe is the iframe's attributes with unique value to identify the iframe.
We can get the id or name of the iframe by checking the DOM of the page as follows:
In the above screenshot, we see that the id of the frame is "frame1 ", so we can use it in code as below -
driver.switchTo().frame("frame1");
where "frame1 " is the id or name of the iframe to which Selenium WebDriver needs to switch the context.
For instance, if you check the ToolsQA demo site, it has two frames, so to get the text of a heading inside the first iframe, we can switch to it using its id as shown in the below code snippet:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//navigate to url
driver.get("https://demoqa.com/frames");
//Switch to Frame using id of the frame
driver.switchTo().frame("frame1");
//Identifying the heading in webelement
WebElement frame1Heading= driver.findElement(By.id("sampleHeading"));
//Finding the text of the heading
String frame1Text=frame1Heading.getText();
//Print the heading text
System.out.println(frame1Text);
//closing the driver
driver.close();
}
}
How to switch to an iframe By WebElement in Selenium?
Selenium identifies each of the HTML elements as WebElements, an object representing an individual web element. The same is the case with iframe; WebDriver can search an iframe using any locator strategies. And then can use the same WebElement object to switch the context to the iframe. WebDriver uses the following syntax to switch the context to an iframe using the WebElement object:
driver.switchTo().frame(WebElement iframeElement)
Where iframeElement is the WebElement returned by the Selenium by searching the same using any given locator strategies.
To understand it better, let's consider an example from the demo site (https://demoqa.com/frames) to get the text inside the iframe whose id is "frame1 ". Here, first, we will get the WebElement using the "id " of the iframe and then use the same to switch to the iframe:
- Navigate the browser to https://demoqa.com/frames
- Get the WebElement object of the iframe using the "id" of the iframe.
- Switch to the iframe using the WebElement object
- Locate the
<h1>
element inside the iframe - Get and print the text present on the iframe, which is "This is a sample page".
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//navigate to url
driver.get("https://demoqa.com/frames");
//Locating frame1 using its id
WebElement frame1=driver.findElement(By.id("frame1"));
//Switching the WebDriver context to frame1
driver.switchTo().frame(frame1);
//Identifying the frame heading in a WebElement
WebElement frame1Heading= driver.findElement(By.id("sampleHeading"));
//Finding the text of the frame1 heading
String frame1Text=frame1Heading.getText();
//Print the heading
System.out.println("Text of the frame1 heading is:"+frame1Text);
//closing the driver
driver.close();
}
}
The output of the above code will print the text "This is a sample page " as shown below -
How to handle nested iframes in Selenium WebDriver?
A browser window can have multiple iframes inside it, like iFrame f1 and iFrame f2. These iframes can further have child iframes like iframe cf1 is inside iframe f1. This arrangement is nested iframes.
In the below image, iframe f1 is the parent of iframe cf1.
If you want to work with a child iframe, you will first have to switch the Selenium WebDriver context to parent iframe f1 and then locate its child iframes. Once we identify the child iframes, you can switch to them using any of the switchTo.frame() methods mentioned in the previous section.
Let's take an example from the ToolsQA Demo site (as shown below ) and learn to switch between nested iframes.
- Navigate the browser:- https://demoqa.com/nestedframes
- Print the number of frames with Selenium WebDriver inside this web page. We should get the count as 1 because we only have one parent frame inside the web page.
- Switch to the parent frame using the id of the frame.
- Print the number of frames using Selenium WebDriver inside the parent frame. We should again get the count as 1 because it has only one child frame.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//Navigate to the demo site
driver.get("https://demoqa.com/nestedframes");
//Number of Frames on a Page
int countIframesInPage = driver.findElements(By.tagName("iframe")).size();
System.out.println("Number of Frames on a Page:" + countIframesInPage);
//Locate the frame1 on the webPage
WebElement frame1=driver.findElement(By.id("frame1"));
//Switch to Frame1
driver.switchTo().frame(frame1);
//Locate the Element inside the Frame1
WebElement frame1Element= driver.findElement(By.tagName("body"));
//Get the text for frame1 element
String frame1Text=frame1Element.getText();
System.out.println("Frame1 is :"+frame1Text);
//Number of Frames on a Frame1
int countIframesInFrame1 =driver.findElements(By.tagName("iframe")).size();
System.out.println("Number of iFrames inside the Frame1:" + countIframesInFrame1);
//switch to child frame
driver.switchTo().frame(0);
int countIframesInFrame2 =driver.findElements(By.tagName("iframe")).size();
System.out.println("Number of iFrames inside the Frame2:" + countIframesInFrame2);
driver.close();
}
}
Output after executing the snippet :
In the above output screenshot, we can see that the number of iframes present on the web page prints as 1. As we had switched to frame1 in the code, it also prints the current context of the WebDriver and then prints the number of iframes present in the Parent frame since there are no frames in the child frame, so the count of iframes inside frame2 (child-frame) is 0.
How to switch the context back to the parent iframe from the child iframe?
Once you have switched to the child iframe, the Selenium WebDriver will hold the current context of the child iframe, and you will not be able to identify elements present in the parent iframe. For example, if you switch to the child frame on https://demoqa.com/nestedframes and then try to execute the below code. You expect it to print "Parent Frame", but in the output, you will get "Child Frame".
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
driver.get("https://demoqa.com/nestedframes");
//Number of Frames on a Page
int countIframesInPage =driver. findElements(By. tagName("iframe")). size();
System.out.println("Number of Frames on a Page:"+countIframesInPage);
//Locate the frame1 on the webPage
WebElement frame1=driver.findElement(By.id("frame1"));
//Switch to Frame1
driver.switchTo().frame(frame1);
//Number of Frames on a Frame1
int countIframesInFrame1 =driver. findElements(By. tagName("iframe")). size();
System.out.println("Number of Frames inside the Frame1:"+countIframesInFrame1);
//Switch to child frame
driver.switchTo().frame(0);
int countIframesInFrame2 =driver. findElements(By. tagName("iframe")). size();
System.out.println("Number of Frames inside the Frame2:"+countIframesInFrame2);
//Locate the Element inside the Frame1
WebElement frame1Element= driver.findElement(By.tagName("body"));
//Get the text for frame1 element
String frame1Text=frame1Element.getText();
//Try to Print the text present inside parent frame
System.out.println("Frame1 is :"+frame1Text);
driver.close();
}
}
The output/ result of the above code will be as below:
Now, if you want to print the output as "Parent Frame", you will have to switch the context of the SeleniumWebDriver back to the parent iframe. This can be done using the method switchTo().parentFrame()
Let's make a change in the above code. We can do so by making the Selenium WebDriver switch the context back to the parent iframe.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//Navigate to the demo site
driver.get("https://demoqa.com/nestedframes");
//Number of Frames on a Page
int countIframesInPage =driver. findElements(By. tagName("iframe")). size();
System.out.println("Number of Frames on a Page:"+countIframesInPage);
//Locate the frame1 on the webPage
WebElement frame1=driver.findElement(By.id("frame1"));
//Switch to Frame1
driver.switchTo().frame(frame1);
//Number of Frames on a Frame1
int countIframesInFrame1 =driver. findElements(By. tagName("iframe")). size();
System.out.println("Number of Frames inside the Frame1:"+countIframesInFrame1);
//Swiitch to child frame
driver.switchTo().frame(0);
int countIframesInFrame2 =driver. findElements(By. tagName("iframe")). size();
System.out.println("Number of Frames inside the Frame2:"+countIframesInFrame2);
//Switch to Parent iFrame
driver.switchTo().parentFrame();
//Locate the Element inside the Frame1
WebElement frame1Element= driver.findElement(By.tagName("body"));
//Get the text for frame1 element
String frame1Text=frame1Element.getText();
//Try to Print the text present inside parent frame
System.out.println("Frame1 is :"+frame1Text);
driver.close();
}
}
The output of the above code will print the Parent Frame text:
Similarly, to work with multiple child iframes using Selenium inside the parent frame, you will first switch the driver context to the parent frame. Once you are in the parent frame, you can switch to the desired child iframe inside the parent iframe.
How to switch the context back to the main web page from the child iframe?
In the previous section, we covered the switching between frames using Selenium. But there are scenarios when you have to get back to the main web page after working with the iframes inside the page.
Selenium WebDriver provides the method switchTo().defaultContent(). It switches the context back to the main page, no matter how deep the current context of the WebDriver is?
Let's understand this by using the demo site https://demoqa.com/nestedframes:
The below code-snippet tries to access the content on the main page, without switching back the context to the main page:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//Navigate to URL
driver.get("https://demoqa.com/nestedframes");
//Locate the webelement page heading
WebElement pageHeadingElement=driver.findElement(By.xpath("//div[@class='main-header']"));
//Find text of the page heading
String pageHeading=pageHeadingElement.getText();
//Print the page heading
System.out.println("Page Heading is :"+pageHeading);
//Switch to Parent iframe
WebElement frame1=driver.findElement(By.id("frame1"));
driver.switchTo().frame(frame1);
WebElement frame1Element= driver.findElement(By.tagName("body"));
String frame1Text=frame1Element.getText();
System.out.println("Frame1 is :"+frame1Text);
//Switch to child frame
driver.switchTo().frame(0);
WebElement frame2Element= driver.findElement(By.tagName("p"));
String frame2Text=frame2Element.getText();
System.out.println("Frame2 is :"+frame1Text);
//Try to print the heading of the main page without swithcing
WebElement mainPageText=driver.findElement(By.xpath("//*[@id=\"framesWrapper\"]/div[1]/text()"));
System.out.println(mainPageText);
driver.close();
}
}
Output: We get NoSuchElementException because currently, WebDriver is in the child iframe. Inside child iframe, there is no such web element that we can identify using the mentioned XPath.
Let's modify the above code snippet. Here, we will first switch the context back to the main page. After that, we will try to access the web element on the web page:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from the local machine path
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
driver.get("https://demoqa.com/nestedframes");
WebElement pageHeadingElement=driver.findElement(By.xpath("//div[@class='main-header']"));
String pageHeading=pageHeadingElement.getText();
System.out.println("Page Heading is :"+pageHeading);
//Switch to Parent frame
WebElement frame1=driver.findElement(By.id("frame1"));
driver.switchTo().frame(frame1);
WebElement frame1Element= driver.findElement(By.tagName("body"));
String frame1Text=frame1Element.getText();
System.out.println("Frame1 is :"+frame1Text);
//Switch to child frame
driver.switchTo().frame(0);
WebElement frame2Element= driver.findElement(By.tagName("p"));
String frame2Text=frame2Element.getText();
System.out.println("Frame2 is :"+frame2Text);
//Switch to default content
driver.switchTo().defaultContent();
//Try to print the heading of the main page without swithcing
WebElement mainPageText=driver.findElement(By.xpath("//*[@id='framesWrapper']/div[1]"));
System.out.println(mainPageText.getText());
driver.close();
}
}
The output/ result of the above code snippet will be:
This way, we can switch the focus of Selenium back to the main web page. Additionally, we can perform the needed actions on the web page elements.
A user might wonder that what is the difference between driver.switchTo().parentFrame() and driver.switchTo().defaultContent() methods? They might look slightly similar in the first glance.
- driver.switchTo().defaultContent(): This will pass the control to the main document which contains the iframes.
- driver.switchTo().parentFrame(): This will pass the control to the immediate parent frame of the current frame.
How to handle dynamic iframes using Selenium WebDriver?
Dynamic iframes are the iframes where properties like iframe id and iframe name, etc., change dynamically on a web page, without any change in the iframe position. In such cases, the fixed value given for the iframe id or name will not identify the iframe.
So, to identify dynamic frames, we can use the below ways :
- The index of the iframe can uniquely identify it based on the frame position.
In the below screenshot, we have two iframes on the page, as shown. Let's try to find the total number of iFrames on a webpage. After that, we will switch to them using an index with the help of the below code snippet.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class IFrameDemo {
public static void main(String[] args){
//set the location of chrome browser from local machine
System.setProperty("webdriver.chrome.driver", "C:\\chromedriver.exe");
// Initialize browser
WebDriver driver=new ChromeDriver();
//navigate to url
driver.get("https://demoqa.com/frames");
//Find the total number of iframes on the page
int totalIFramesOnPage=driver.findElements(By.tagName("iframe")).size();
//Print the total iframes on page
System.out.println("Total iframes on Page:"+totalIFramesOnPage);
//switch to first frame using index=0
driver.switchTo().frame(0);
driver.close();
}
}
The output of the above code will print 2 iframes located on the page -
- In some cases , the name or id attribute might contain a prefixed values. For example, in the below code:
<iframe id = ‘iframe_1001>…</iframe> OR <iframe name= ‘iframe_ABC'>…</iframe>.
the value "iframe_" is constant and is not changing with different frames. You can construct an effective XPath using the ' contains' keyword like //iframe[contains(@id,'frame')] Or //iframe[contains(@name,'frame')] and then locate the iframe using that XPath.
So this way, we can locate the dynamic iframes in Selenium. Then switch the focus to them for locating the web elements inside the iframes.
Key Takeaways
- iframes are embedded into an HTML page using
<iframe>
tag. - To work with different nested iframes, we can use the method switchTo() to switch the frames using Index, Name, or Id & Web Element.
- Moreover, you can switch to immediate parent iframe using method switchTo().parentFrame().
- You can switch to the main web page from child iframe using method switchTo().defaultContent().
- Additionally, you can use a frame index to switch to dynamic frames. Because they do not have a fixed value for id or name attribute.