Selenium Page Locator Strategies and NoSuchElementException
A beginner-friendly guide to Selenium locator strategies — ID, class, CSS, XPath — and how to avoid NoSuchElementException.
Press Listen. A recorded voice reads the article, lights the current word, and keeps that word in view.
How listen mode works
The recording is a neural voice, not your browser's speech engine. The word being spoken lights up from the audio clock, including after you pause, drag the bar, or change speed. If you chose UK and only the US recording exists, you hear the US voice. Leaving the page stops playback.
I’ve spent way too many 2 AM nights hunting for a button that the test swears doesn’t exist — even though I can see it on my screen. NoSuchElementException. The bane of every automation engineer. And nine times out of ten, the culprit isn’t the button. It’s how I asked for it.
This post is about the right way to ask the DOM for elements. It’s the difference between a test suite that survives UI changes and one that breaks every sprint.
Why Locator Choice Matters
Think of the DOM as a filing cabinet. XPath lets you search by ancestry — “go up to the grandparent, then down to the third cousin.” CSS searches forward only — “start from the top drawer and follow a path.” Both find the file, but one is faster and more stable.
Poor locator strategy means:
- Tests fail mysteriously when the UI shifts (a real burden early in a project)
- Performance degrades — XPath searches are expensive on big DOMs
- Maintenance becomes a nightmare — update the HTML, hunt through 50 tests to find the broken selector
- Synchronization issues creep in — you’re waiting for something you can’t even reliably find
The right choice, made once and encoded in page objects, pays dividends for years.
Finding Elements — Your Preference Ladder
You’ve got choices. Each has a tradeoff.
The Selenium 3.0 Classics
ID — If it exists, use it. IDs are unique, static, and the browser finds them instantly.
Class Name — Common, stable, and fast. Classes don’t change as often as DOM structure.
CSS Selectors — The sweet spot for most of us. Fast, readable, and lets you navigate from a stable parent down to your target. A real win when the HTML shifts.
XPath — Powerful but expensive. XPath can walk backward through the tree (to parents, siblings) and forward. That flexibility comes at a cost — the browser has to traverse more slowly. You’ll need XPath for edge cases, but CSS is your daily driver.
Rough speed ranking: ID > Class > CSS » XPath. In practice: if your page is stable, it doesn’t matter. If it changes frequently, CSS will keep you sane.
Selenium 4.0 — Relative Locators (Your Escape Hatch)
Relative locators let you say: “Give me the button near the text ‘Submit’” or “the input below the ‘Email’ label.” They’re game-changers for fragile UIs because they describe relationships, not paths.
// No brittle nth-child. No absolute XPath. Just "the input below the label"
driver.locateElement(RelativeLocator.withTagName("input").below(By.id("email-label")));
These catch changes that would shatter traditional selectors.
Setting Up WebDriver — The Plumbing
Before you can find a single element, WebDriver needs to know which browser to drive. There are two ways: the old way (manual path hunting) and the modern way (automatic).
Step #1 — Point WebDriver to the Browser
Using System.setProperty:
System.setProperty("webdriver.chrome.driver", "/path/to/driver.exe"); // Setup Webdriver binary
Or use Bonigarcia’s WebDriverManager so the binary resolution is automatic:
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${LATEST.version}</version>
<scope>test</scope>
</dependency>
Step #2 — Swap WebDriver Interface With a Fully Featured Browser
WebDriver webDriverInstance = new ChromeDriver();
Synchronization — The Missing Puzzle Piece
A good locator is half the battle. The other half: waiting for the element to be there.
The DOM doesn’t load all at once. JavaScript runs. Animations finish. Data arrives. Your test needs to wait for these things. Miss this, and you’ll blame your selectors for failures that are really timing issues.
Three strategies. Use them in order.
Strategy #1: Implicit Waits (Blunt Instrument)
WebDriver webDriverInstance = new ChromeDriver();
webDriverInstance.navigate().to("https://www.google.com/");
webDriverInstance.manage().timeouts().pageLoadTimeout(Long.valueOf(20), TimeUnit.SECONDS);
webDriverInstance.manage().timeouts().implicitlyWait(Long.valueOf(20), TimeUnit.SECONDS);
This says: “Before you throw NoSuchElementException, wait up to 20 seconds for the element to appear.” It’s simple and broad — every single element search waits 20 seconds if not found immediately. Downside: it’s always waiting, even when you don’t need it.
Strategy #2: Explicit Waits (Surgical — Use This)
new WebDriverWait(webDriverInstance, Long.valueOf(20))
.until(ExpectedConditions.titleIs("Google"));
This is better. You explicitly say: “Wait up to 20 seconds for the page title to be ‘Google’.” It waits only when you ask, and only for what you ask. The moment the condition is true, it continues. No unnecessary pauses.
Strategy #3: FluentWait (For Edge Cases)
FluentWait lets you customize polling intervals and which exceptions to ignore. Save it for complex waits. In 90% of cases, explicit WebDriverWait is enough.
/*
* Wait will ignore instances of NotFoundException that are encountered (thrown) by default in
* the 'until' condition, and immediately propagate all others. You can add more to the ignore
* list by calling ignoring(exceptions to add).
*/
assertTrue(
new WebDriverWait(webDriverInstance, Long.valueOf(20))
.until(ExpectedConditions.titleIs("Google"))
.booleanValue(),
"*****Url not loaded*****");
A Real Example — Google’s Search Box
All these strategies find the same element. The question is: which one should you pick?
By searchByName = By.name("q");
By searchByCssSelector = By.cssSelector(".a4bIc > input[role='combobox']");
By searchByAbsoluteXpath = By.xpath("/html/body/div/div[4]/form/div[2]/div[1]/div[1]/div/div[2]/input");
By searchByRelativeXpath = By.xpath("//input[@title='Search']");
Let me rank them for you:
By.name("q")— Fastest. Thenameattribute is stable. Use this.By.cssSelector— Also fast and readable, but tied to the CSS class structure. If Google refactors their styles, this breaks.By.xpath(relative) —//input[@title='Search']is flexible but slower than CSS.By.xpath(absolute) — Don’t. Ever. This shatters if Google moves the input to a different nesting level. Absolute XPath is debugging hell.
When XPath Wins
XPath can go backward through the tree. Need the form that contains a specific button? XPath can walk up to the parent. CSS can’t.
// "Find the form that contains a button labeled 'Submit'"
By.xpath("//form[.//button[text()='Submit']]");
CSS stops there — it goes only forward down the tree. This is XPath’s one genuine advantage.
When CSS Wins
CSS is faster and more readable for forward-only navigation. It’s your default. Reach for XPath only when you genuinely need backward traversal.
A Tricky Case — Tables
Both CSS and XPath can navigate tables. CSS is again faster.
// CSS — faster, readable
By.cssSelector("table#table1 tr:nth-of-type(2) > td:nth-of-type(5)");
// XPath — more powerful but slower
By.xpath("(//tr[2])[1]//td[5]");
CSS is my go-to for tables. XPath shines if you need to find which row contains a specific cell (backward traversal), then grab a column from that row. But if you know the row number, CSS wins.
Final Word
I can tell you from experience: the locators you write today will outlast the UI they target. You’ll spend more time maintaining your selectors than writing them. That’s why the choice matters.
Here’s what I do:
- Prefer IDs and stable attributes — if they exist, use them.
- Fall back to CSS — fast, readable, sufficient for 90% of cases.
- Use XPath only for backward traversal — it’s the only thing CSS can’t do.
- Set up explicit waits — they’re your insurance against timing ghosts.
- Encode it in page objects — one change, one place, then your whole suite stays green.
Make these decisions once. Encode them as page-object helpers. Then when the designer moves a button or the PM adds a new field, you fix it in one place, not thirty tests.
Sources & Further Reading
- Selenium locators — official guide
- Relative locators — Selenium 4
- Waits and synchronization
- NoSuchElementException — troubleshooting
See also: Selenium in 2026: A Beginner’s Guide (Jul 2026) · XPath for Test Automation (Jul 2026) — the story-mode article with complex XPath & CSS for SDETs (SVG, computed indices, ARIA chains, iframe/shadow-DOM, modern CSS); the natural next read after this 2020 strategy-ladder introduction.
Add a thought
The writing box stays shut until the code matches. A note you save shows up under this article on this browser. It is not emailed. Posting it for everyone opens GitHub, which asks you to sign in.