Answer
XPath vs CSS Selector in Selenium
CSS Selectors (Preferred for Most Cases)
Java
// ID
By.cssSelector("#submit-btn") // faster than By.id()
// Class
By.cssSelector(".error-message")
By.cssSelector(".btn.btn-primary") // multiple classes
// Attribute
By.cssSelector("[data-testid='login']")
By.cssSelector("input[type='email']")
By.cssSelector("[placeholder='Enter email']")
By.cssSelector("[href*='login']") // href CONTAINS 'login'
By.cssSelector("[class^='btn-']") // class STARTS WITH 'btn-'
By.cssSelector("[class$='-primary']") // class ENDS WITH '-primary'
// Parent → Child (direct)
By.cssSelector("form > input") // input is DIRECT child of form
// Ancestor → Descendant
By.cssSelector("div.container input") // input anywhere inside div.container
// nth-child
By.cssSelector("table tr:nth-child(2)") // 2nd row
By.cssSelector("ul li:first-child")
By.cssSelector("ul li:last-child")
// Combination
By.cssSelector("div.login-form input[type='password']")
XPath Selectors
Java
// Absolute (fragile — never use)
By.xpath("/html/body/div/form/input") // ❌ breaks on any DOM change
// Relative (always use)
By.xpath("//input[@id='email']")
By.xpath("//button[@type='submit']")
By.xpath("//div[@class='login-form']//input[@type='email']")
// By TEXT CONTENT (XPath unique advantage)
By.xpath("//button[text()='Submit']")
By.xpath("//button[contains(text(),'Sign')]")
By.xpath("//label[normalize-space(text())='Email Address']")
// Parent traversal (XPath unique advantage)
By.xpath("//input[@id='email']/parent::div") // go UP to parent
By.xpath("//input[@id='email']/ancestor::form") // go UP to ancestor
By.xpath("//label[text()='Email']/following-sibling::input") // sibling
By.xpath("//tr[td[text()='Alice']]//button[@class='edit']") // row by cell
// Index (1-based in XPath!)
By.xpath("(//input[@type='text'])[2]") // 2nd text input
// contains() — partial match
By.xpath("//div[contains(@class,'error')]")
By.xpath("//a[contains(@href,'logout')]")
// Multiple conditions (and / or)
By.xpath("//input[@type='text' and @required]")
By.xpath("//button[@id='ok' or @id='confirm']")
// starts-with()
By.xpath("//input[starts-with(@id,'user-')]")
Key Differences
| CSS Selector | XPath | |
|---|---|---|
| Speed | Faster (browser-native) | Slightly slower |
| Readability | Cleaner | Verbose |
| Text matching | ❌ Not supported | ✅ text(), contains(text()) |
| Parent traversal | ❌ Not supported | ✅ parent::, ancestor:: |
| Preceding sibling | ❌ Not supported | ✅ preceding-sibling:: |
| Index | :nth-child(n) | [n] (1-based) |
| Browser support | All browsers | All browsers |
When to Use Each
Java
// Use CSS when:
// - ID, class, attribute, or structural location
By.cssSelector("[data-testid='login-btn']") // attribute
By.cssSelector("table tr:nth-child(3) td") // table cell
By.cssSelector(".nav-menu > li:first-child a") // nested structure
// Use XPath when:
// - Need to match by text
By.xpath("//button[text()='Delete']")
// - Need to traverse UP the DOM (find parent)
By.xpath("//input[@id='search']/ancestor::form")
// - Need preceding-sibling
By.xpath("//td[text()='Alice']/preceding-sibling::td[1]")
// - Dynamic content where text is the only stable identifier
By.xpath("//span[contains(text(),'Order #')]")
Best Practice Hierarchy
- ✓
By.id()— most reliable, fastest - ✓
By.cssSelector("[data-testid='x']")— test-specific attributes - ✓
By.cssSelector()— general CSS - ✓
By.xpath()— when text matching or parent traversal needed - ✓Never:
By.xpath("/html/body/...")— absolute paths
