Locating page elements using WebDriver August 31, 2011
Tweet
Types of Locators
Locators have changed with Selenium 2 with the WebDriver API. Out of the box WebDriver supports several ways to locate elements on the page using the By
abstract class.
Finding elements by ID
ids are the preferred way to locate elements on a page for 2 main reasons:
- According to w3c ids are supposed to be unique on an html page. This makes ids a very explicit and reliable way to locate elements on the page.
- Also, all browsers also have highly efficient methods to get an object on the page using their ids. This makes
id
locators the fastest type of locator in Selenium.
Let us now look at how to use id locators using this html code as an example:
<form name="loginForm"> Login Username: <input id="username" name="login" type="text" /> Password: <input id="password" name="password" type="password" /> <input name="login" type="submit" value="Login" /> </form>
In the above code, the username and password text fields are can be set using their ids. The locators for them would be
driver.findElement(By.id(username));
Even though this is a great locator, it is not realistic for all elements on a page to have ids. The developers add ids to key elements on the page to better control the look and feel or provide the dynamic user interaction. However, ids should also be added to elements that are frequently interacted within tests … to make the pages more testable. Automated test script authors should consider adding, or requesting addition of, ids to these key elements on the page.
In some cases, the ids on an element cannot be reliably used in a test. For instance, if you are displaying objects stored in the database, the objects ids could contain the database id in it, for instance book1347
could be the id for book who’s database id is 1347. In such situations it may not be possible to predict and use the ids and so you may need to use other ways described below to find elements.
Finding elements by name
Generally id
s are added to elements when they want to be referenced from css or javascript and name
s are added to form fields. When referencing element from javascript either can be used. From a test automation standpoint, whenever id is not available/ usable, you should try to use the name instead.
Using the same example above, the way you would find the submit button would be:
driver.findElement(By.name("login"));
There is one big difference between the id
and name
attributes though … name attributes don’t have to be unique in a page. If there are multiple elements with the same name, then the first element in the page is selected. So, in this example, if another button or form named “login” was present of added later, it could cause the test to fail.
Finding links by text
This locator identifies links by the text in them. Let us look at an example:
<html> <body> ... <a href="signin.html">Sign In</a> to modify your account details. ... </body> </html>
To click this hyperlink using the anchor tag’s text, you can use the By.linkText()
locator:
driver.findElement(By.linkText("Sign In"));
If there are multiple elements with text “Sign In”, the first one is selected.
Btw, this is called linkText
because it is used for hyperlinks. In Selenium if you used the link=textPattern
locator, you could use it to locate other elements like div, span, td etc. In WebDriver, this locator works only for links.
Another common case is when we need to find links by a portion of the text it contains. In such cases you can find it by specifying the partial text. For example:
driver.findElement(By.partialLinkText("Sign"));
Finding elements by XPath
XPath is a very powerful language to express which element to identify. If you use it correctly, it can produce very reliable and low maintenance locators, but if you use it incorrectly, it can create very brittle test cases.
Let us see some examples:
<table name="cart"> <tr id="item1"> <td class="name item">Mp3 Download: foobar.mp3</td> <td class="name item"><input name="qty" class="name item formfield disabled" /></td> </tr> <tr id="item2"> <td class="name item">Mp3 Player</td> <td class="name item"><input id="item2_quantity" name="qty" class="name item formfield required" type="text" /></td> </tr> ... </table>
You can target the highlighted input field using the following XPath expressions:
//table/tr[2]/td/input
//input[@id='item2_quantity']
(//table[@name='cart']//input)[2]
//input[contains(@class,'required')]
//input[contains(@class,'required') and type='text']
And for the last option, your java call would look like:
driver.findElement(By.xpath("//input[contains(@class='required') and type='text']"));
As you can guess, some of these expressions will not be as reliable as others. Of these //table/tr[2]/td/input
is the worst because it would break even with slightest modification to the page structure. It can take some time to learn XPath if you aren’t familiar with it, but it is worth the time if you plan to spend a lot of time writing UI automated tests. In any case do not rely on tools, including selenium IDE, to generate the right xpath expression for you. They can help you get started but they are usually bad at identifying the more reliable XPaths.
XPath namespaces
There are some special cases you should be aware of when you work with XPath, like when you are trying to interact with SVG. Like in the example here, the html looks something like this:
<div id="svgchart"> ... <svg xmlns="http://www.w3.org/2000/svg"> <g> <path .../> </g> ... </svg> ... </div>
Here, if you use XPath like:
//svg
you will get ERROR org.openqa.selenium.NoSuchElementException: Unable to locate element
. This is because the svg element is in a different namespace. You will have to specify your xpath with the namespace uri like this instead:
//*[local-name()='svg' and namespace-uri()='http://www.w3.org/2000/svg']
XPath performance
So, if XPath’s are so versatile, why doesn’t everyone prefer these? It’s because they are often the slowest, especially in older versions of IE! There are some ways you can make XPath’s faster, but they still are a few times slower than ids or names.
Finding elements by CSS
css
locators can be used to identify a large number of elements on a page. Let us look at this html snippet for instance:
<table name="cart"> <tr id="item1"> <td class="label">Mp3 Download: foobar.mp3</td> <td class="item"><input name="qty" class="formfield disabled" /></td> </tr> <tr id="item2"> <td class="label">Mp3 Player</td> <td class="item"><input id="item2_quantity" name="qty" class="formfield required" type="text" /></td> </tr> ... </table>
In this case the css
locator variations possible for the highlighted input field are:
input.required
input[class~='required']
input.required[type='text']
#item2_quantity
input#item2_quantity
The css locator may not be as expressive as XPath, but it generally executes faster.
Finding elements by class
This is more of a convinience mechanism to identify elements. In the css example above, instead of using
driver.findElements(By.css("input[class~='required']"));
you could use
driver.findElements(By.class("required"));
Getting elements by DOM
DOM stands for Document Object Model. DOM is convention for representing objects in HTML documents.
<form id="loginForm"> Login Username: <input id="username" name="username" type="text" /> Password: <input name="password" type="password" /> <input name="login" type="submit" value="Log in" /> </form>
In this page, the dom expression for the highlighted input field would be:
document.forms[0].elements[0]
document.forms['loginForm'].elements['username']
document.forms['loginForm'].username
document.getElementById('username')
For those who have used Selenium 1 API, you would expect to find a By.dom() equivalent api, but it doesn’t exist. However, you still can get a handle to these elements using dom expressions by using the following code snippet instead:
... driver.findElement(byDom("document.forms[0].elements[0]")); ... public By byDom(String domExpression) { final Object o = ((JavascriptExecutor) driver).executeScript("return " + domExpression + ";"); if (o instanceof WebElement) { return new By() { @Override public List<WebElement> findElements(SearchContext searchContext) { return new ArrayList<WebElement>() { { add((WebElement) o); } }; } }; } }
The three key aspects of this code that you should note,
- The driver can be casted to
JavascriptExecutor
and other interfaces which will provide additional capabilities. executeScript()
inJavascriptExecutor
returns an object which can be casted to aWebElement
if the javascript expression returns a dom element.- You can create your own implementation of
By
Some things to note for Selenium 1 users
- You will notice that Implicit locators are not supported in WebDriver.
- When using name in Selenium 1, you could add other attributes in the expressions to filter the results. For example
name=login type=text
was valid. You cannot do that any more with the By.name() call.
findElement()
and findElements()
As you might have noticed in the examples above, the By
locators were used within findElement
and findElements
methods. The purpose of these methods is to return a WebElement
, in case of findElement
, and a list of WebElement
s in the case of findElements
.
The WebElement
represents an html element on the page. In the next section you will see that it is this WebElement
object that you would interact with or read attributes of for validations etc.
When you use these methods on the WebDriver
object, the scope within with the elements are located is the entire page.
Getting child elements
The WebElement
interface also has findElement()
and findElements()
methods. In this case, it is within the scope of this parent element that child elements are located. This is useful when you want to narrow the scope to find several elements that you would interact with. In most cases, narrowing the scope improves the execution times for locating the elements.
Hi,
I was wondering when “Interacting with page elements” will be published.
This is the fist location I’ve found that actually explains things properly.
I’ve been trying to understand how findElements() works and I’m hoping this section will do it.
Tx
Peter
Thanks for your comment. I am hoping to publish it in the next few days.
Very precise and informative, I found this really helpful,
waiting for the next article.
Thanks,
Pallavi
Thanks Pallavi. I added the Interacting with html page elements using WebDriver recently. Feedback is appreciated!
Really help me a lot…appreciated!
I am novice to WEBDRIVER…Thanks alot… It helped me a lot on how to user WEBDRIVER to identifying the objects..
It’s really a great job and I learnt a lot of things about SELENIUM from this document. Thanks a lot
Hi, Could you please help me with the Selenium wait commands instead of using Thread.sleep(2000)? I tried in so many ways in order to achieve it, but it was not successful. I hope i will get the solution for this.
Awesome documentation . Great job Rahul .i could learn many new things
am looking forward for any help , to locate svg elements .I have searched in many forums but found no solution . Could you kindly help me
?
I use python with Selenium
I have tried elem = self.browser.find_element_by_xpath(“//svg[@class=’sap-piechart’]/path[@title=’Handhelds : 1′]”)
but element not found.I need to click on each element within the tag with different title
HTML code looks like this
One of the best articles on XPATH I’ve come across.
Very helpful.
Thank you.
You should rename ‘Finding elements by text’ to ‘Finding links by text’. I was searching to find elements by text but this does not work unless it is a link. I am trying to click an item in a java script drop down menu without specific id. So I can’t use link text to locate it because it is a . I usually us a css contains “li:contains(‘my text’)” but that does not seem to work anymore.
Awesome, you have the best tutorials. Very beginner-friendly, much appreciated.
Thanks Scott. I modified the post to say find links by text and not find elements by text. I can’t say with certainty, but, I think it might have even located elements that were not links when I was trying Selenium-WebDriver when it was in beta.
The
li:contains('my partial text')
works if you use the Sizzle js library. You can always inject that library with WebDriver and then use that syntax.Nice tutorial,As a beginner quite helpful for me!
Hi Rahul,
thanks a lot for your article
but I ‘m very confused findElement(); and findElements();
it’s easy to understand findElement();
but how to use findElements(); or what can findElements(); do ?
eg:
Mp3 Download: foobar.mp3
Mp3 Player
…
when I use findElements(BY.name(‘cart’));
it will return a list that contains what ? children notes of this table ?
god…
my example was not published successful…
please refer the css locator section of your article
sorry for this inconvenience
I am trying to select a radio button which is within a browser
I’m using
driver.FindElement(By.CssSelector(“//*[@id=’Dealer’]”)).Click();
so
Please help me in selecting the radio button.
Hey yedukondalu,
driver.FindElement(By.xpath(“//*[@id=’Dealer’]“)).Click();
driver.FindElement(By.CssSelector(“*[id=’Dealer’]“)).Click();
driver.FindElement(By.id(“Dealer“)).Click();
you can use any one of these
You mentioned that when using By.linkText() selenium will select the element for the first time the link appears. Is there an easy way to have it select the second time the link appears?
Hi All,
I am trying automate a mail-engine..
Able to locate all the fields except the area where we write the mail content…tried using By.* 🙁
could u assist..
Regards
GiRi
to be more precise…I am trying automate compose a mail…
Able to locate : to, subject ,send etc…But not able to locate the composing area /text area
Regards
GiRi
Hi Raj,
I am using selenium webdriver with in java application but I am tring click on radio buttons with in a chrome browser then doe’s not selecting radio button. so please help me.
My test case is:-
Browser.findElement(By.id(“type”).click;
greatly helpful!!!thanks!!!
Very nice article to understand how webdriver interact with web elements
Hi there!
In Telerik it’s possible to search elements within an element that was already found. E.g. I found a , that has some elements. After that I can invoke find() directly from the element.
Is there such possibility using the WebDriver?
awesome information ……
Hi, I am not using Webdriver API, but directly controlling Selenium RC using PERL.
a) Let us say I was able to invoke google.com using Perl, how do I use $Locator and Click($Locator) commands to actually clock on “Sign In ” button of Google.
b) Sometimes webpages have two data entry fields.
How do I type into a particulat data entry field again using Locator and type($locator,$value) commands
Can somebody kindly give examples for these ?
c) How do I install and use Webdriver API on top of Selenium RC ?
Hi i am having problem to click on a element on javascript. In HTML ID for that element is not given only Class Name is mention. I am using following code
public static void JseClickById(String selector, IJavaScriptExecutor JSE)
{
if(selector==PROFILE_WINDOWCLOSE_Xpath)
{
//JSE.ExecuteScript(“document.getElementByxpath(‘” + selector + “‘).click();”);
// JSE.ExecuteScript(“$(arguments[0].click()”, selector);
Driver.FindElement(By.XPath(“//a[contains(@class,’selector’)]”)).Click();
}
else
{
JSE.ExecuteScript(“window.document.getElementById(‘” + selector + “‘).click();”);
}
}
Can anybody help me with this. How to use Xpath or Class name in the string which i am giving to ExecuteScript??
Thanks in Advance
Very helpful!
suppose iam writing for email field my css will be
findElement(By.cssSelector(“input[value=”#EmailID”]”).getText(“eshwar@gmail.com”)……
is it correct
Hi Rahul,
I am facing lot of problems related to XPath. Can you please let me know any good site, book…etc to learn XPath.
Hi,
Its good to understand the identifier. But i’m not able to find and handle a object identity. Can someone help me- See, there is a movie web apps and its dynamic contents .. There are three fields – Region, Movie and Date. all are drop down and after selecting region Movie fields are enable then Date will be enable. each week movie has changed and date is changing. when we search movie then getting show time in result. But some time some cinemas is not there and sometimes movie is not in that specified region or cinema.
SO how can we handle this condition ?
hi,
why cant we just use selece scripts instesd of exporting/generating the test scripts in java,php or html
i think selence scripts are easy and less complex
example:
type //td[@id=’first_name-bodyEl’]/input sa
type //td[@id=’last_name-bodyEl’]/input khanna
clickAt //div[2]/table[2]/tbody/tr/td[2]/table/tbody/tr/td[2]/div
verifyText//li[3] Supplies Office
click //li[3]
type //td[@id=’item_name-bodyEl’]/input richa
click //table[5]/tbody/tr/td[2]/table/tbody/tr/td[2]/div
verifyText //li[7] Kids Accessories
click //li[7]
click //table[6]/tbody/tr/td[2]/table/tbody/tr/td[2]/div
verifyText //tr[4]/td/a/em/span 20
click //tr[4]/td/a
click //div[11]/div[3]/div/div/div/em/button
Hi,
I am using python script and facing issue with Elements which have same name which have different Xpath.
Can some one help me.
To give you more idea.. am finding elements from a table view and entering data into text boxes.
So I have text boxes with name labels like Country and state, which indeed have different Xpath.
When i use find_element_by_name and send data its works fine for the first element but not next element.
Is CSS locator possible for this xpath:
driver.findElement(By.xpath(“//span[contains(text(), ‘Submit’)]”)).click();
“Submit” is the text on button.
Please help it’s urgent.
If two elements have the same attributes and same values,which method is prefered to locate the element.Please give example
Hi ,
I have multiple clickable option in div tag while on on clickable option I ma not able to move to another clickable tab.I am using Findby (xapth ) but its throwing the exception by saying Nosuchelement found exception.
Hi Rahul,
I am trying to automate Seleniumhq.org site for practice purpose. When I Click on Documentation Tab/Link on top of the page it works fine with FireFox but Using IE, I get ‘NoSuchElementException’. What could be the reason.