
Selenium Locators Masterclass
Locators are the foundation of reliable test automation. Choosing the right locator strategy determines whether your tests are stable and maintainable or constantly breaking due to minor UI changes. This masterclass covers every locator type in Selenium, when to use each, and patterns that professional automation engineers rely on.
The golden rule of locator selection: Use CSS when you can, switch to XPath when you must.
Table Of Contents-
Understanding Locators
A locator is a query that identifies one or more elements on a web page. WebDriver uses locators to:
- Find elements to interact with (click, type, select)
- Find elements to extract information from (text, attributes)
- Verify element presence, visibility, or count
How WebDriver Finds Elements
// Find single element (throws exception if not found)
WebElement element = driver.findElement(By.locator);
// Find multiple elements (returns empty list if none found)
List<WebElement> elements = driver.findElements(By.locator);What Makes a Good Locator?
| Quality | Description |
|---|---|
| Unique | Identifies exactly one element (for findElement) |
| Stable | Doesn't change with minor UI updates |
| Readable | Other team members can understand it |
| Performant | Browser can resolve it quickly |
Locator Strategies Overview
Selenium supports eight locator strategies:
| Strategy | Syntax (Java) | Best For |
|---|---|---|
| ID | By.id("value") | Elements with unique ID |
| Name | By.name("value") | Form fields |
| Class Name | By.className("value") | Single-class elements |
| Tag Name | By.tagName("value") | Element type |
| Link Text | By.linkText("value") | Exact link text |
| Partial Link Text | By.partialLinkText("value") | Partial link text |
| CSS Selector | By.cssSelector("value") | Complex queries |
| XPath | By.xpath("value") | Any element |
Priority Recommendation
- ID - Most reliable when available
- data-testid attributes (via CSS/XPath)
- Name - Good for form fields
- CSS Selector - Fast, flexible
- XPath - When CSS can't do it
ID Locator
The ID locator is the fastest and most reliable when IDs are:
- Unique on the page
- Stable (not dynamically generated)
HTML
<input type="text" id="username" name="user" />
<button id="submit-btn">Submit</button>Usage
// Java
WebElement username = driver.findElement(By.id("username"));
WebElement submitBtn = driver.findElement(By.id("submit-btn"));# Python
username = driver.find_element(By.ID, "username")
submit_btn = driver.find_element(By.ID, "submit-btn")Limitations
- Many elements don't have IDs
- Dynamic IDs (e.g.,
id="field_12345") are unstable - Duplicate IDs (invalid HTML, but common) cause issues
⚠️
Watch for Dynamic IDs: If IDs contain random numbers or session-specific values, they'll change between runs. Use CSS partial matching or other locators instead.
Name Locator
The Name locator targets the name attribute, commonly used in forms.
HTML
<input type="email" name="email" id="user-email" />
<input type="password" name="password" />
<select name="country">
...
</select>Usage
// Java
WebElement email = driver.findElement(By.name("email"));
WebElement password = driver.findElement(By.name("password"));
WebElement country = driver.findElement(By.name("country"));When to Use
- Form fields consistently have
nameattributes - Names are usually stable (tied to backend processing)
- Multiple elements can share the same name (radio buttons)
Class Name Locator
Locates elements by their CSS class. Only works with a single class name.
HTML
<button class="btn btn-primary submit-button">Submit</button>
<div class="error-message">Invalid input</div>Usage
// Works - single class
WebElement error = driver.findElement(By.className("error-message"));
// Won't work - multiple classes
// WebElement btn = driver.findElement(By.className("btn btn-primary")); // Error!
// Use CSS Selector for multiple classes
WebElement btn = driver.findElement(By.cssSelector(".btn.btn-primary"));Limitations
- Cannot match multiple classes
- Classes often aren't unique
- Styling classes (like
btn-primary) may change
Tag Name Locator
Finds elements by HTML tag type.
Usage
// Find all links
List<WebElement> links = driver.findElements(By.tagName("a"));
// Find all inputs
List<WebElement> inputs = driver.findElements(By.tagName("input"));
// Find table rows
List<WebElement> rows = driver.findElements(By.tagName("tr"));Best Use Cases
- Counting elements of a type
- Finding elements within a container
- When combined with other filters
// Find links within a specific section
WebElement nav = driver.findElement(By.id("navigation"));
List<WebElement> navLinks = nav.findElements(By.tagName("a"));Link Text Locators
Specifically for <a> (anchor) elements.
HTML
<a href="/login">Sign In</a> <a href="/help">Need Help? Contact Support</a>Link Text (Exact Match)
// Must match exactly
WebElement signIn = driver.findElement(By.linkText("Sign In"));Partial Link Text
// Matches if text contains this string
WebElement help = driver.findElement(By.partialLinkText("Contact Support"));
WebElement help2 = driver.findElement(By.partialLinkText("Need Help"));Limitations
- Only works for
<a>tags - Text might change (translations, rewording)
- Whitespace and case sensitivity issues
CSS Selectors
CSS Selectors are powerful, fast, and readable. They should be your go-to for complex locators.
Basic CSS Selectors
| Selector | Syntax | Matches |
|---|---|---|
| ID | #myId | id="myId" |
| Class | .myClass | class="myClass" |
| Tag | div | <div> elements |
| Multiple classes | .class1.class2 | Both classes |
| Tag with class | input.error | <input class="error"> |
Attribute Selectors
// Exact attribute match
By.cssSelector("[name='email']")
By.cssSelector("input[type='submit']")
// Attribute contains
By.cssSelector("[id*='partial']") // Contains 'partial'
// Attribute starts with
By.cssSelector("[id^='user']") // Starts with 'user'
// Attribute ends with
By.cssSelector("[id$='_field']") // Ends with '_field'
// Has attribute (any value)
By.cssSelector("[disabled]")
By.cssSelector("input[required]")Hierarchy Selectors
// Direct child
By.cssSelector("form > input") // Input directly inside form
// Descendant (any level)
By.cssSelector("form input") // Input anywhere inside form
// Adjacent sibling
By.cssSelector("label + input") // Input immediately after label
// General sibling
By.cssSelector("h2 ~ p") // P elements after h2Pseudo-Classes
// First/last child
By.cssSelector("ul li:first-child")
By.cssSelector("ul li:last-child")
// Nth child
By.cssSelector("tr:nth-child(2)") // Second row
By.cssSelector("tr:nth-child(odd)") // Odd rows
By.cssSelector("tr:nth-child(even)") // Even rows
// Not selector
By.cssSelector("input:not([type='hidden'])")Practical Examples
// Login form fields
By.cssSelector("#login-form input[name='username']")
By.cssSelector("form#login input[type='password']")
// Data attributes (recommended for test automation)
By.cssSelector("[data-testid='submit-button']")
By.cssSelector("[data-qa='user-profile']")
// Table cells
By.cssSelector("table#users tbody tr:nth-child(3) td:nth-child(2)")XPath Selectors
XPath is the most powerful locator strategy - it can find any element. Use it when CSS isn't sufficient.
Basic XPath Syntax
// Absolute path (fragile - avoid)
By.xpath("/html/body/div/form/input")
// Relative path (recommended)
By.xpath("//input") // Any input
By.xpath("//form//input") // Input inside formAttribute Selection
// By attribute
By.xpath("//input[@id='username']")
By.xpath("//input[@type='submit']")
// Multiple attributes
By.xpath("//input[@type='text' and @name='email']")
By.xpath("//input[@type='text' or @type='password']")
// Contains
By.xpath("//input[contains(@id, 'partial')]")
By.xpath("//input[contains(@class, 'error')]")
// Starts-with
By.xpath("//input[starts-with(@id, 'user')]")Text-Based Selection (XPath Exclusive)
// Exact text match
By.xpath("//button[text()='Submit']")
By.xpath("//a[text()='Sign In']")
// Contains text
By.xpath("//p[contains(text(), 'Welcome')]")
By.xpath("//div[contains(., 'Error')]") // . includes child text
// Normalize space (handles whitespace)
By.xpath("//button[normalize-space()='Submit']")XPath Axes (Navigation)
XPath can navigate in any direction - CSS cannot.
// Parent
By.xpath("//input[@id='email']/..") // Parent of email input
By.xpath("//input[@id='email']/parent::div") // Parent div
// Ancestors
By.xpath("//input[@id='email']/ancestor::form")
// Following sibling
By.xpath("//label[text()='Email']/following-sibling::input")
// Preceding sibling
By.xpath("//input[@id='email']/preceding-sibling::label")
// Following (any element after)
By.xpath("//h2/following::p[1]") // First p after h2Practical XPath Examples
// Find button by text
By.xpath("//button[text()='Login']")
// Find element relative to label
By.xpath("//label[text()='Username']/following-sibling::input")
// Find table row containing text
By.xpath("//tr[contains(., 'John Smith')]")
// Find element with specific child
By.xpath("//div[contains(@class, 'alert')][contains(., 'Error')]")XPath Text Selection: Only XPath can select elements by their text
content. If you need to find <button>Submit</button> by the word "Submit",
XPath is your only option.
CSS vs XPath Comparison
| Feature | CSS Selector | XPath |
|---|---|---|
| Speed | Faster | Slower |
| Readability | More readable | Can be complex |
| Navigate up DOM | Cannot | Can (parent, ancestor) |
| Text matching | Cannot | Can (text(), contains()) |
| Browser support | Native | Varies |
| Index selection | Limited | Flexible |
When to Use CSS
- Simple attribute matching
- Class and ID selection
- Child/descendant navigation
- Performance is critical
When to Use XPath
- Finding elements by text content
- Navigating to parent elements
- Complex conditional logic
- Following/preceding sibling navigation
Best Practices
1. Prefer Stable Attributes
// Good - data-testid is stable
By.cssSelector("[data-testid='login-button']")
// Avoid - class names change for styling
By.className("btn-blue-lg-rounded")2. Keep Locators Short
// Good - specific and short
By.cssSelector("#user-table tr:nth-child(3) .edit-btn")
// Avoid - overly specific
By.cssSelector("body > div.container > section > div.row > div.col-md-8 > table#user-table > tbody > tr:nth-child(3) > td > button.edit-btn")3. Use Unique Identifiers
// Good - unique combination
By.cssSelector("form#checkout input[name='card-number']")
// Risky - might match multiple elements
By.cssSelector("input[type='text']")4. Add Test-Specific Attributes
Work with developers to add data-testid attributes:
<button data-testid="submit-order" class="btn btn-primary">Place Order</button>By.cssSelector("[data-testid='submit-order']")5. Validate Locators in DevTools
Before using a locator in code:
Chrome DevTools Console:
// Test CSS selector
$$('css-selector-here')
// Test XPath
$x('xpath-here')6. Create a Locator Strategy Guide
Document your team's conventions:
- data-testid naming conventions
- When to use CSS vs XPath
- How to handle dynamic elements
- Locator review checklist
Test Your Knowledge
Quiz on Selenium Locators
Your Score: 0/10
Question: Which locator strategy should be your first choice when the element has a unique, stable ID?
Continue Your Selenium Journey
Frequently Asked Questions
Frequently Asked Questions (FAQs) / People Also Ask (PAA)
Should I use CSS Selectors or XPath in Selenium?
Why do my locators keep breaking?
What are data-testid attributes and should I use them?
How can I find elements by their text content?
What's the difference between findElement() and findElements()?
How do I handle dynamic elements with changing IDs?
How can I test if my locator works before using it in code?
Why is By.className() giving me an error with multiple classes?