CSS Selectors
By.cssSelector() lets you locate elements using the same CSS syntax browsers use to
apply styles. It's more flexible than id/name/className
alone: you can combine an element's tag, class, attributes, and position relative to other elements
all in a single expression. Because CSS selectors are evaluated by the browser's own native
selector engine, they're also typically faster than the equivalent XPath expression (covered
in the next lesson).
Core CSS Selector Syntax
| Syntax | Meaning | Example |
|---|---|---|
#id | Element with this id | #email |
.class | Element with this class | .recipe-card |
tag | Element with this tag name | input |
[attr=value] | Element with an exact attribute value | [type='email'] |
[attr^=value] | Attribute value starts with | [id^='recipe-'] |
A B (space) | B is a descendant of A, at any depth | .recipe-card h3 |
A > B | B is a direct child of A | ul.recipe-list > li |
A, B | Matches either A or B | #email, #password |
:nth-child(n) | The n-th child of its parent | li:nth-child(2) |
Worked Example: Locating the Search Box
RecipeBox's search input, by id, is the same in CSS syntax as it was with By.id():
// <input type="text" id="searchInput">
driver.findElement(By.cssSelector("#searchInput")).sendKeys("Butter Chicken");
// same element, but matched by attribute instead of id
driver.findElement(By.cssSelector("input[type='text'][id='searchInput']")).sendKeys("Butter Chicken");
Note: when a plain
id is available, By.id("searchInput")
reads more clearly than the CSS equivalent By.cssSelector("#searchInput") — reach
for CSS selectors once you need more than a bare id/name/class match.Worked Example: Recipe List Items
Suppose RecipeBox renders its recipe list like this:
// <ul class="recipe-list">
// <li class="recipe-card" data-category="main"><h3>Paneer Butter Masala</h3></li>
// <li class="recipe-card" data-category="main"><h3>Butter Chicken</h3></li>
// <li class="recipe-card" data-category="dessert"><h3>Gulab Jamun</h3></li>
// </ul>
// Every recipe card, direct children of the list
List<WebElement> allCards = driver.findElements(By.cssSelector("ul.recipe-list > li.recipe-card"));
// Only dessert recipes, matched by a data attribute
List<WebElement> desserts = driver.findElements(By.cssSelector(".recipe-card[data-category='dessert']"));
// The title text inside the second recipe card
WebElement secondTitle = driver.findElement(By.cssSelector("li.recipe-card:nth-child(2) h3"));
System.out.println(secondTitle.getText()); // "Butter Chicken"
Reading it back to front helps:
li.recipe-card:nth-child(2) h3
means "find an h3, that is a descendant of, an li with class
recipe-card that is the 2nd child of its parent." CSS selectors read left-to-right as
"outer element, then inner element," which can feel backwards at first if you're used to thinking
"find the h3 inside the 2nd card."Why CSS Selectors Are Often Preferred Over XPath
- Performance: the browser's own native
querySelectorengine evaluates CSS selectors directly; XPath in a browser context typically runs through a slower translation layer. - Readability: most engineers (not just testers) already know CSS syntax from writing stylesheets, so CSS locators are easier for a whole team to read and maintain.
- Conciseness: matching by class or attribute is usually shorter in CSS
(
.recipe-card[data-category='dessert']) than the equivalent XPath.
Where XPath still wins: CSS selectors can only travel down the DOM
(parent to child/descendant) and cannot select an element based on its own text content. XPath can do
both — select a parent based on a child, or find an element by its visible text. We'll cover
exactly that trade-off in the next lesson.
Next up: for the cases CSS selectors can't handle — matching by visible
text, or walking upward from a child to a parent — head to
XPath.