Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
WebElement 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Selenium’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.

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.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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-Verdict and X-Billed headers.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.