Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use an explicit wait that describes the state your next action requires, and pass a locator to that wait. For a JavaScript-inserted element, wait with presenceOfElementLocated; for an element that exists but is hidden, use visibilityOfElementLocated; for a click, prefer elementToBeClickable. Click the WebElement returned by the wait instead of keeping a reference found before the page changed.
This pattern handles the race between Selenium’s navigation completion and a single-page application that continues rendering. A completed readyState does not prove that a dynamically created control is present, visible, enabled, or unobstructed.
The reliable Java pattern
Start with a finite WebDriverWait and a By locator. Selenium evaluates the locator repeatedly until the condition succeeds or the timeout expires.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement target = wait.until(
ExpectedConditions.elementToBeClickable(By.id("submit")));
target.click();
The ten-second value is an example, not a universal delay. Choose a limit that fits the application’s documented behavior and keep it finite so a broken locator or failed load becomes an actionable test failure.
#1 Best Overall
Use the Selenium APIs documented in Waiting Strategies, Expected Conditions, the ExpectedConditions Java API, and the Wait Java API.
Choose the condition that matches the DOM state
| What is true now? | Condition | What it guarantees | Typical next step |
|---|---|---|---|
| The node has not been inserted yet | presenceOfElementLocated |
At least one matching node is in the DOM; it may still be hidden or disabled | Inspect it, wait for another state, or perform a non-visual operation |
| The node exists but is not displayed yet | visibilityOfElementLocated |
The matching element is displayed with usable dimensions | Read text, inspect attributes, or prepare an interaction |
| The next operation is a click | elementToBeClickable |
The element is visible and enabled | Click the returned element |
Presence alone does not make a click safe. A present element can be hidden, disabled, covered by a modal, or replaced immediately after it was found. Clickability is clearer when clicking is the actual requirement.
Wait for an element inserted after an action
When a button causes JavaScript to create a new element, perform the action first, then wait by locator. Do not call findElement before insertion and expect the reference to become valid later.
driver.findElement(By.id("adder")).click();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement added = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("box0")));
added.click();
Selenium’s official demonstration uses a two-second wait for an element added after a click. That number illustrates the API, not a recommended timeout for every site. In production, select a timeout appropriate to your application and retain a condition that communicates the required state.
Presence-only example
WebElement node = wait.until(
ExpectedConditions.presenceOfElementLocated(By.cssSelector("[data-test='result']")));
String status = node.getAttribute("data-status");
Use this when the DOM node itself is the contract, such as reading an attribute that is available before the element is painted. If the test must interact with what a user sees, wait for visibility or clickability instead.
Rank #2
Wait for a hidden element to become visible
Some applications render a control immediately and reveal it only after validation, animation, or another click. Waiting for insertion will return too early. Wait for visibility:
driver.findElement(By.id("show-details")).click();
WebElement details = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("details")));
details.click();
Selenium’s Java guidance also supports a custom lambda when the application’s notion of visibility is more specific:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsWebElement details = wait.until(d -> {
WebElement element = d.findElement(By.id("details"));
return element.isDisplayed() ? element : null;
});
Returning null tells WebDriverWait to poll again. Keep the lookup inside the lambda so a newly rendered node can be found on each poll.
Click only when the control is ready
elementToBeClickable combines visibility and enabled state:
WebElement save = wait.until(
ExpectedConditions.elementToBeClickable(By.cssSelector("button[type='submit']")));
save.click();
It does not prove that a third-party overlay will not intercept the pointer at the element’s center. A cookie dialog, loading mask, sticky header, or animation can still produce ElementClickInterceptedException. Wait for the obstructing element to disappear, close it deliberately, or change the locator to the actual actionable control.
Waiting for an overlay to disappear
wait.until(ExpectedConditions.invisibilityOfElementLocated(
By.cssSelector(".loading-mask")));
WebElement submit = wait.until(
ExpectedConditions.elementToBeClickable(By.id("submit")));
submit.click();
If the overlay is optional, model that explicitly rather than adding a blind sleep. A condition tied to the application state is faster on a fast run and safer on a slow one.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
Prevent stale-element failures when the DOM is redrawn
A WebElement is a reference to one particular DOM node. If a framework replaces that node during re-rendering, the reference does not relocate itself and Selenium can throw StaleElementReferenceException. The fix is to locate by By inside the wait and use the element returned by that wait:
WebElement row = wait.until(
ExpectedConditions.elementToBeClickable(By.cssSelector("tr[data-id='42']")));
row.click();
Avoid this fragile sequence:
WebElement row = driver.findElement(By.cssSelector("tr[data-id='42']"));
// React, Vue, or another framework replaces the row here
wait.until(ExpectedConditions.elementToBeClickable(row)).click();
If replacement can happen between the condition and the click, wrap the complete operation in a retry that re-finds the locator, while limiting retries so a persistent application defect remains visible:
By locator = By.id("submit");
for (int attempt = 0; attempt < 2; attempt++) {
try {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
break;
} catch (org.openqa.selenium.StaleElementReferenceException e) {
if (attempt == 1) throw e;
}
}
Why fixed sleeps and page-load completion fail
Thread.sleep pauses for a duration, not for a condition. A short sleep fails on a slow CI run; a long one wastes time when the element is ready immediately. Selenium defines explicit waits as polling for a condition until success or timeout.
Navigation’s readiness state covers resources declared by the original document. Client-side JavaScript can fetch data, mount components, and replace markup afterward. Therefore, a page reported as loaded can still lack the button your test needs.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Selenium’s documentation states: “Do not mix implicit and explicit waits.” An implicit wait changes how every findElement call polls, while an explicit wait adds its own polling and timeout. Combining them can produce unpredictable effective delays; the official guide illustrates a nominal ten-second implicit wait and fifteen-second explicit wait taking up to twenty seconds.
Rank #4
Use the default zero implicit wait with explicit conditions, or adopt one consistent strategy across the suite. Do not compensate for an uncertain locator by increasing every timeout.
Diagnose the exception instead of increasing the timeout
| Symptom | Likely cause | Targeted fix |
|---|---|---|
TimeoutException while waiting for presence |
The insertion never occurred, the locator is wrong, the request failed, or the element is inside a frame or shadow root | Check the locator in browser devtools, inspect network/application state, and switch to the correct frame or shadow-root API |
| Presence succeeds but visibility times out | CSS keeps the node hidden, an ancestor is hidden, or the UI waits for another state | Wait for the display condition that the application uses; do not click a hidden node with JavaScript as a shortcut |
ElementNotInteractableException |
The node is present but not displayed or enabled | Use visibility or clickability and verify disabled attributes and validation state |
ElementClickInterceptedException |
An overlay, cookie banner, sticky element, or animation covers the click point | Wait for invisibility, dismiss the overlay, scroll appropriately, or wait for the animation to finish |
StaleElementReferenceException |
The framework replaced the node after you located it | Discard the old reference and re-find by locator inside the wait |
| The locator works on the top document but never in the test | The target is inside an iframe or shadow DOM | Switch to the frame first; for shadow DOM, obtain the shadow root and query within it |
Selenium’s common-errors guide describes these interaction failures and the role of explicit waits. Capture a screenshot, page source, current URL, and relevant console or network logs when a timeout occurs; those artifacts distinguish timing from a genuinely broken page.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Locator and wait design for maintainable tests
Prefer stable, user-facing hooks
Use an accessibility role, an associated label, a stable id, or a dedicated data-testid rather than a generated CSS class or a deep XPath. A stable locator reduces apparent timing problems caused by matching the wrong node.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Keep the timeout local to the behavior
A page-wide “wait ten seconds after every action” hides which state the application promises. Put the wait next to the action and choose a finite value for that operation. If a workflow legitimately has a longer server-side job, wait for its progress or completion indicator rather than sleeping.
Return the object your next line needs
Conditions that return a WebElement make the state transition explicit and avoid a second lookup that can race with a redraw. For text or attributes, return the element after the relevant state is true; for a click, return the click-ready element.
Best Value
Or skip the browser setup
If your actual goal is a rendered image or PDF rather than an interaction, ScreenshotNeo provides a single screenshot request without maintaining Selenium drivers. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
For API parameters and all capture options, see the ScreenshotNeo documentation. A direct call is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
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)
And Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Every plan includes its features, including full-page lazy-image loading, CSS-selector element capture, device and viewport controls, custom JavaScript and CSS, waits, request blocking, cookies and headers, geolocation, PDF output, caching, signed links, asynchronous webhooks, bulk capture for up to 100 URLs per call, usage data, and an OpenAPI specification. The Free plan includes 1,000 screenshots each month without a card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Performance, reliability, and cost considerations
- Polling a precise condition usually finishes sooner than a fixed sleep because it stops as soon as the state is true.
- Use one driver session per test isolation policy, but do not carry stale element references across navigation or component redraws.
- Keep waits finite and log the locator and condition when they fail; this turns intermittent reports into diagnosable evidence.
- Do not use JavaScript
arguments[0].click()to bypass a real interaction failure. It can hide overlays, disabled controls, or inaccessible UI that a user cannot click. - For screenshots or PDFs, ScreenshotNeo bills only clean captures; bot checks, blank pages, timeouts, failed loads, and cache hits are free, with the result exposed in
X-Page-VerdictandX-Billedheaders.
FAQ
Frequently Asked Questions
Should I wait for the page’s network idle state instead of an element?
Network activity can stop before a component is inserted or become quiet while a control is still disabled. Wait for the specific DOM state required by the next action; use network or application-idle signals only when they are part of that application’s contract.
Can I store a WebElement in a page-object field?
You can, but a field becomes invalid when navigation or a framework redraw replaces the node. Store a By locator and resolve it through an explicit wait at the point of use when replacement is possible.
Why does elementToBeClickable still produce an intercepted-click error?
The condition checks visibility and enabled state, not every overlay or layout race. Wait for known masks or dialogs to become invisible and investigate what covers the element’s click point.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Is a two-second timeout ever correct?
It is suitable for Selenium’s documentation example, but it is not a general recommendation. Set a finite timeout based on your application and test environment, then let a timeout expose an unmet assumption.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

