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

SyntaxMeaningExample
#idElement with this id#email
.classElement with this class.recipe-card
tagElement with this tag nameinput
[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 > BB is a direct child of Aul.recipe-list > li
A, BMatches either A or B#email, #password
:nth-child(n)The n-th child of its parentli: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 querySelector engine 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.