How to Use CSS Selectors in Selenium Tests
Learn to write reliable Selenium CSS selectors, choose the right locator, handle repeated matches and shadow DOM, and fix InvalidSelectorException.
Use Selenium’s CSS locator strategy to pass a CSS selector string to the browser: in Python, driver.find_element(By.CSS_SELECTOR, "#fname"); in Java, driver.findElement(By.cssSelector("#fname")). Start with a stable, specific selector, then verify that it matches the intended element in the current page or search context. If you need all matches, use the plural lookup. Selenium’s locator guide recommends a well-written CSS selector when a unique ID is unavailable. Selenium locator documentation.
1. Find an element by CSS selector in Selenium
Inspect the rendered DOM, choose a selector that expresses the element you need, and pass it to Selenium’s CSS locator API. The selector is CSS syntax; it is not an XPath expression or a raw ID value.
Python: runnable example
from selenium import webdriver
from selenium.webdriver.common.by import By
# Requires Selenium Python bindings and a browser driver available to Selenium.
driver = webdriver.Chrome()
try:
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
# CSS ID selector: the # is part of CSS syntax.
first_name = driver.find_element(By.CSS_SELECTOR, "#my-text-id")
first_name.send_keys("Ada")
# CSS attribute selector.
password = driver.find_element(By.CSS_SELECTOR, "input[name='my-password']")
password.send_keys("example")
finally:
driver.quit()
The Selenium locator example page documents selecting an element by ID and selecting an input by name. Use the current markup on the page under test; IDs and attributes in examples will not necessarily exist on another site. If your test uses the fname ID, its CSS selector is #fname.
Java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class CssLocatorExample {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://www.selenium.dev/selenium/web/web-form.html");
WebElement firstName = driver.findElement(By.cssSelector("#my-text-id"));
firstName.sendKeys("Ada");
} finally {
driver.quit();
}
}
}
JavaScript
const { Builder, By } = require('selenium-webdriver');
(async function example() {
const driver = await new Builder().forBrowser('chrome').build();
try {
await driver.get('https://www.selenium.dev/selenium/web/web-form.html');
const firstName = await driver.findElement(By.css('#my-text-id'));
await firstName.sendKeys('Ada');
} finally {
await driver.quit();
}
})();
These examples use the Selenium bindings’ documented CSS locator forms: Python By.CSS_SELECTOR, Java By.cssSelector, and JavaScript By.css. Install the Selenium binding for your language and configure a supported browser and driver for your environment before running them.
2. Write selectors for IDs, classes, and attributes
| What you want | CSS selector | Notes |
|---|---|---|
An element with ID fname |
#fname |
The hash is CSS syntax. An ID locator takes the raw value fname. |
An element with class information |
.information |
May match multiple elements. |
An input with name newsletter |
input[name='newsletter'] |
Combines a tag with an attribute condition. |
| An element with a specific attribute value | [data-state='ready'] |
Attribute selector syntax is [attribute=value]. |
| An input with both a class and name | input.field[name='email'] |
Combines tag, class, and attribute conditions. |
Prefer the smallest clear selector that uniquely identifies the intended element in the relevant context. A long chain tied to incidental wrapper structure is harder to understand and maintain. Use attributes that your application keeps meaningful; inspect the rendered DOM rather than assuming a particular attribute is stable.
For example, if an ID is unique and suitable, you can choose either By.ID, "fname" or By.CSS_SELECTOR, "#fname". The ID strategy expects the raw ID. The CSS strategy expects a complete CSS selector.
3. Choose CSS, ID, or XPath deliberately
| Locator | Good fit | Keep in mind |
|---|---|---|
| ID | A unique, useful ID is present. | Pass the raw ID value, such as fname. |
| CSS selector | You need a well-written selector based on IDs, classes, or attributes. | Use CSS syntax such as #fname or input[name='email']. |
| XPath | You need a relationship or selection that suits XPath. | Use the XPath locator strategy, for example //input[@value='f'], not the CSS strategy. |
Selenium recommends a well-written CSS selector when a unique ID is unavailable. Its guidance says XPath can be flexible but harder to debug and tends to be slow, while noting that browser vendors typically do not performance-test XPath selectors. Treat this as Selenium’s guidance, not a universal browser benchmark. Team consistency and selector readability matter more than an assumed speed advantage.
4. Handle multiple matches and no matches
find_element returns the first matching element. That is convenient only when the selector is intended to identify that first match or is unique. A broad selector can silently return a different element than the test intended.
from selenium.webdriver.common.by import By
elements = driver.find_elements(By.CSS_SELECTOR, ".information")
if not elements:
raise AssertionError("No elements matched .information")
# Assert uniqueness when the test expects exactly one match.
if len(elements) != 1:
raise AssertionError(f"Expected one match, found {len(elements)}")
information = elements[0]
The plural finder returns all matches and an empty list when there is no match. If the test needs one particular member of a repeated group, tighten the selector or locate the group first and search within that element. Avoid relying on “first” unless document order is part of the intended behavior.
5. Search within a parent or shadow root
A page-level lookup searches from the document context. Selenium also lets you search from a previously located element, which can reduce ambiguity when several components contain similar controls.
from selenium.webdriver.common.by import By
card = driver.find_element(By.CSS_SELECTOR, "article.product-card")
add_button = card.find_element(By.CSS_SELECTOR, "button.add-to-cart")
Shadow DOM has a separate search boundary. A normal page-level CSS selector does not automatically pierce it. Locate the shadow host, obtain its shadow root, then search inside that root. Selenium documents shadow-root finder methods for Selenium 4.0 and later and describes browser support in relation to Chromium v96.
from selenium.webdriver.common.by import By
host = driver.find_element(By.CSS_SELECTOR, "settings-panel")
shadow_root = host.shadow_root
save_button = shadow_root.find_element(By.CSS_SELECTOR, "button.save")
If a selector appears correct but finds nothing, check whether the desired node is inside a shadow root, iframe, or another search context. Switch to the appropriate context before querying.
6. Wait for the page state your selector needs
A valid selector can still return no match when the page has not rendered the target yet. For elements that appear asynchronously, wait for the expected condition instead of adding an arbitrary sleep.
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
email = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "input[name='email']"))
)
email.send_keys("ada@example.com")
The wait timeout should reflect the application and test environment. Waiting longer can reduce false failures when rendering is genuinely delayed, but it cannot fix a selector that targets the wrong context or an element that never appears.
7. Fix InvalidSelectorException and other locator failures
| Symptom | Likely cause | What to check |
|---|---|---|
InvalidSelectorException |
Malformed CSS, invalid characters, unmatched brackets or quotes. | Check punctuation and close every bracket and quote. |
InvalidSelectorException |
Selector language does not match locator strategy. | Send CSS such as #fname through the CSS strategy; send XPath such as //input[@value='f'] through XPath. |
| No such element | Valid selector, but no matching element in the current page state or search context. | Inspect current markup, wait for dynamic content, and verify frame or shadow-root context. |
| Wrong element found | Selector matches several elements and singular lookup returns the first. | Use plural lookup to inspect matches, assert the expected count, or scope/tighten the selector. |
| ID lookup fails | A CSS selector was passed to an ID locator, or vice versa. | Use By.ID, "fname" for the raw value or By.CSS_SELECTOR, "#fname" for CSS. |
Selenium’s error guidance identifies invalid syntax and a mismatch between selector type and locator strategy as common reasons for InvalidSelectorException. A no-match condition is different: the selector may be valid but the node may not exist in the current state or context.
8. Keep selectors reliable and tests efficient
- Choose a selector that communicates the element’s purpose and is as specific as necessary.
- Prefer meaningful IDs or attributes already present in the application over long chains based on incidental nesting.
- Use singular lookup when uniqueness is expected and make that expectation explicit; use plural lookup when the page is meant to contain a collection.
- Scope repeated component selectors to a parent element when that reflects the test’s intent.
- Wait for a condition tied to the element’s actual state rather than sleeping for a fixed interval.
- Keep CSS and XPath syntax paired with their corresponding locator strategies.
- Do not infer universal performance from selector type. Selenium’s documentation offers comparative guidance, but this dossier contains no benchmark figures.
Selector lookup itself is only one part of test cost. Browser startup, page loading, and synchronization often dominate an end-to-end test; keep browser sessions purposeful and avoid redundant navigations. For reliability, make failures informative by reporting the selector and expected match count.
Or skip the browser setup
If your task is to capture a page screenshot rather than interact with it in a Selenium test, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF. See the ScreenshotNeo API documentation for options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents use tools for screenshots, page information, and PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
How do I find an element by CSS selector in Selenium?
Use the binding’s CSS locator API: for example, Python driver.find_element(By.CSS_SELECTOR, "input[name='email']").
Should I use CSS or XPath?
Use a clear CSS selector for common ID, class, and attribute cases. Use XPath when its expression model is useful, and keep the locator strategy aligned with the syntax.
Does a CSS selector search inside shadow DOM automatically?
No. Find the host, get its shadow root, and run the selector from that root using Selenium 4 or later.
Why does a valid selector return the wrong element?
The selector may match multiple nodes. Check the collection with plural lookup and make the selector or search context more specific.


