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.

🎯 What you'll learn
  • 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>
Advertisement

CSS Selectors Cheat Sheet (Tested)

The third column shows exactly what each selector matched on the page above:

CSS selectorTypeMatched element(s)
#usernameBy IDinput#username[name=username]
.btn-primaryBy classbutton#login-btn-8f3a "Log in"
input[name="password"]By attributeinput#password[name=password]
[data-testid="login-email"]By test attributeinput#username[name=username]
form#loginForm input[type="text"]Tag + id + attributeinput#username[name=username]
.btn.btn-primaryElement with two classesbutton#login-btn-8f3a "Log in"
[id^="login-btn"]Attribute starts withbutton#login-btn-8f3a "Log in"
a[href$="password"]Attribute ends witha "Forgot password?"
[placeholder*="Email"]Attribute containsinput#username[name=username]
#loginForm > buttonDirect childbutton#login-btn-8f3a "Log in"
form inputAny descendantinput#username[name=username], input#password[name=password], input[name=remember]
#username + inputNext siblinginput#password[name=password]
ul.results li:nth-child(2)nth-childli "Playwright"
li.item:first-childFirst childli "Selenium"
li.item:last-childLast childli "Cypress"
li:not(.active)not()li "Selenium", li "Cypress"
input:checkedChecked stateinput[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

OperatorMeaningExample
^=Starts with[id^="login-btn"] — handles dynamic ids like login-btn-8f3a
$=Ends witha[href$="password"]
*=Contains[placeholder*="Email"]
=Exactly equalsinput[name="password"]

CSS Selector vs XPath

CSS selectorXPath
ReadabilityShort and cleanLonger
Find by visible textNoYes: //button[text()="Log in"]
Go up to a parentNoYes: .. or ancestor::
SpeedFastFast in modern browsers
Best forid, class, attributes, structureText, 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

  1. Spaces in a class name: By.className("btn btn-primary") fails, because className takes one class. Fix: By.cssSelector(".btn.btn-primary").
  2. Using generated ids like #login-btn-8f3a that change on every build. Fix: [id^="login-btn"] or a stable data-testid.
  3. Long, brittle paths such as div > div > form > button. One layout change breaks them. Fix: anchor on an id, class or attribute.
  4. Expecting CSS to find text. :contains() is not valid CSS in browsers. Fix: use XPath for text.
  5. 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

  1. On the sample page, write a selector for the "Remember me" checkbox in two different ways.
  2. Write a selector for every list item except the first.
  3. 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"].