A CSS selector is a pattern that finds elements on a web page by their tag, id, class or attributes. In Selenium you pass it to By.cssSelector(). CSS selectors are short, fast and readable, which is why most automation teams prefer them to XPath whenever an element can be described without its text.
- How to write CSS selectors for id, class and any attribute
- Partial matches: starts with, ends with and contains
- Parent/child, sibling, nth-child and
:not()selectors - How to use them in Selenium with Java — and when XPath is the better choice
CSS Selector Syntax in Selenium
In Java, every selector goes into By.cssSelector(...):
WebElement email = driver.findElement(By.cssSelector("#username"));
All the selectors in this guide were tested in a real browser against this sample login page:
<!DOCTYPE html><html><body>
<form id="loginForm" class="form auth-form">
<input id="username" name="username" type="text" placeholder="Email address" data-testid="login-email">
<input id="password" name="password" type="password" placeholder="Password" data-testid="login-password">
<label><input type="checkbox" name="remember" checked> Remember me</label>
<button id="login-btn-8f3a" class="btn btn-primary" type="submit">Log in</button>
<a href="/forgot-password" class="link">Forgot password?</a>
</form>
<ul class="results">
<li class="item">Selenium</li><li class="item active">Playwright</li><li class="item">Cypress</li>
</ul>
</body></html>
CSS Selectors Cheat Sheet (Tested)
The third column shows exactly what each selector matched on the page above:
| CSS selector | Type | Matched element(s) |
|---|---|---|
#username | By ID | input#username[name=username] |
.btn-primary | By class | button#login-btn-8f3a "Log in" |
input[name="password"] | By attribute | input#password[name=password] |
[data-testid="login-email"] | By test attribute | input#username[name=username] |
form#loginForm input[type="text"] | Tag + id + attribute | input#username[name=username] |
.btn.btn-primary | Element with two classes | button#login-btn-8f3a "Log in" |
[id^="login-btn"] | Attribute starts with | button#login-btn-8f3a "Log in" |
a[href$="password"] | Attribute ends with | a "Forgot password?" |
[placeholder*="Email"] | Attribute contains | input#username[name=username] |
#loginForm > button | Direct child | button#login-btn-8f3a "Log in" |
form input | Any descendant | input#username[name=username], input#password[name=password], input[name=remember] |
#username + input | Next sibling | input#password[name=password] |
ul.results li:nth-child(2) | nth-child | li "Playwright" |
li.item:first-child | First child | li "Selenium" |
li.item:last-child | Last child | li "Cypress" |
li:not(.active) | not() | li "Selenium", li "Cypress" |
input:checked | Checked state | input[name=remember] |
Using CSS Selectors in Selenium with Java
This example fills a login form with CSS selectors and waits for the button before clicking. It compiles with Selenium 4.25:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.util.List;
public class CssSelectorsDemo {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver(); // Selenium Manager finds the driver
try {
driver.get("https://example.com/login"); // your application's login page
driver.findElement(By.cssSelector("#username")).sendKeys("tester@example.com");
driver.findElement(By.cssSelector("input[name='password']")).sendKeys("Secret123");
driver.findElement(By.cssSelector("[data-testid='login-email']")).clear();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement login = wait.until(
ExpectedConditions.elementToBeClickable(By.cssSelector("button.btn.btn-primary")));
login.click();
List<WebElement> items = driver.findElements(By.cssSelector("ul.results li"));
System.out.println("Found " + items.size() + " results");
} finally {
driver.quit();
}
}
}
How it works: findElement returns the first match and throws NoSuchElementException if there is none; findElements returns a list (empty when nothing matches); WebDriverWait with elementToBeClickable waits for the button instead of failing straight away.
Partial Attribute Matches
| Operator | Meaning | Example |
|---|---|---|
^= | Starts with | [id^="login-btn"] — handles dynamic ids like login-btn-8f3a |
$= | Ends with | a[href$="password"] |
*= | Contains | [placeholder*="Email"] |
= | Exactly equals | input[name="password"] |
CSS Selector vs XPath
| CSS selector | XPath | |
|---|---|---|
| Readability | Short and clean | Longer |
| Find by visible text | No | Yes: //button[text()="Log in"] |
| Go up to a parent | No | Yes: .. or ancestor:: |
| Speed | Fast | Fast in modern browsers |
| Best for | id, class, attributes, structure | Text, parents, complex conditions |
Rule of thumb: use id or a test attribute such as data-testid first, a CSS selector second, and XPath when you need text or a parent.
Common Mistakes
- Spaces in a class name:
By.className("btn btn-primary")fails, because className takes one class. Fix:By.cssSelector(".btn.btn-primary"). - Using generated ids like
#login-btn-8f3athat change on every build. Fix:[id^="login-btn"]or a stabledata-testid. - Long, brittle paths such as
div > div > form > button. One layout change breaks them. Fix: anchor on an id, class or attribute. - Expecting CSS to find text.
:contains()is not valid CSS in browsers. Fix: use XPath for text. - Not checking the selector first. In Chrome DevTools, open the Console and run
document.querySelectorAll("your selector")to see exactly what it matches.
Practice Exercises
- On the sample page, write a selector for the "Remember me" checkbox in two different ways.
- Write a selector for every list item except the first.
- On any login page, find the password field with CSS and with XPath, and decide which is more stable.
Key Takeaways
#is for id,.is for class, and[attr="value"]is for any attribute.^=,$=and*=handle dynamic attribute values.- Prefer id, data-testid or CSS; switch to XPath for text or parent navigation.
📚 Official documentation: Selenium docs: Locator strategies · MDN: CSS selectors
Frequently Asked Questions
What is a CSS selector in Selenium?
A pattern that locates elements by tag, id, class or attribute, used with By.cssSelector(). For example By.cssSelector("#username") finds the element whose id is username.
Is CSS selector faster than XPath?
CSS selectors are short and fast, but in modern browsers the speed difference is tiny. Choose the one that gives the most stable, readable locator.
Can a CSS selector find an element by its text?
No. CSS has no text matching in browsers. Use XPath, such as //button[text()="Log in"].
How do I select an element with multiple classes?
Chain the classes with dots and no spaces: .btn.btn-primary.
How do I handle dynamic ids with CSS?
Match the stable part with ^= (starts with), $= (ends with) or *= (contains), e.g. [id^="login-btn"].