skip to content

In Selenium, which two checks does ExpectedConditions.elementToBeClickable actually perform before returning?

level: juniorimportance: must knowfreq 74%

answer

  1. Fewer checks than the name suggests
  2. Two booleans on one element
  3. Rendering first, then form state
  4. Nothing asks what is painted above
  5. isDisplayed then isEnabled, and stop

basics

~10 s

Just two: isDisplayed and isEnabled. Selenium's elementToBeClickable locates the element, confirms the browser renders it and that it is not disabled, then returns it. Nothing in the condition asks whether something covers it.

solid answer

~40 s

`ExpectedConditions.elementToBeClickable` is thinner than its name. On each poll it locates the element, calls `isDisplayed()`, then calls `isEnabled()`; if either is false the poll returns `null` and `WebDriverWait` sleeps and tries again. When both are true it returns the `WebElement` and `until` hands it back. That is the whole contract — “clickable” here means *shown and not disabled*, not *reachable by a pointer*. On a cinema showtime picker, a `19:45` button under a translucent “checking seat availability” panel is displayed and enabled, so the wait is satisfied on its first poll and the very next `click()` can still fail. Read it as a signal about the element itself, never as a promise about what is painted on top of it.

code

java · 21 lines
java
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;

public class ShowtimePicker {

  public static void bookSevenFortyFive(WebDriver driver) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    By slot = By.cssSelector(".showtime[data-time='19:45']");

    WebElement showtime = wait.until(ExpectedConditions.elementToBeClickable(slot));

    System.out.println(showtime.isDisplayed());
    System.out.println(showtime.isEnabled());

    showtime.click();
  }
}

go deeper

for a junior

Be ready to name the two checks without hedging: isDisplayed and isEnabled. Knowing that the condition returns the element itself, rather than a boolean, is the other half interviewers listen for.

for a middle

Explain the mechanics: each poll locates, checks the two booleans, and returns null to keep polling. Say which exceptions the poll swallows, and note that the two overloads differ only in whether they re-locate.

for a senior

Show that you design around the gap. Demonstrate that you pick predicates whose truth actually implies the next command can succeed, and that you can name why a passing wait is still followed by a failing click.

for a principal

Own the guidance that a wait predicate is a statement about one element and never about the page. Where a codebase treats elementToBeClickable as a safety guarantee, that assumption is the thing to name and remove.

## The condition is two boolean calls, in order `ExpectedConditions.elementToBeClickable` lives in Selenium 4's Java support library (`org.openqa.selenium.support.ui`), and its body is far smaller than its name suggests. Every time `WebDriverWait` polls it, the `By` overload runs exactly this sequence: 1. Call `driver.findElement(locator)`. If that raises `NoSuchElementException` or `StaleElementReferenceException`, the condition records a "was not found" message and returns `null`, which tells the wait to sleep and poll again. 2. Call `element.isDisplayed()`. If it is `false`, the condition records "was not visible" and returns `null`. 3. Call `element.isEnabled()`. If it is `false`, the condition records "was not enabled" and returns `null`. 4. Otherwise return the located `WebElement`, which ends `until` and becomes the value handed back to your test. There is no fifth step. **In Selenium's vocabulary "clickable" means displayed and enabled** — and that is the whole of it. The recorded message is only used to build the `TimeoutException` text if the wait runs out; it never adds a check. ## What each of the two checks actually answers - **`isDisplayed()`** is a rendering question answered by the browser. It accounts for `display: none`, `visibility: hidden`, a zero-width or zero-height box, and hidden ancestors. It is a statement about the element itself. - **`isEnabled()`** is a form-state question. On a form control it reports the inverse of the `disabled` state; on an ordinary element it is simply `true`, which means a `<div>` styled to look like a button passes this check unconditionally. - Neither call knows anything about **stacking order, overlays, pointer-events, animation, or what is painted on top of the target**. Those are properties of the page around the element, and the condition never asks the page about them. - Neither call is a promise about the future. It is a reading taken at one poll, on one element. ## The two overloads, and the one way they differ | Overload | Re-locates on every poll | A stale reference | Returns from `until` | |---|---|---|---| | `elementToBeClickable(By)` | yes — `findElement` runs each poll | caught, treated as "not yet"; the next poll re-finds, so a re-render is survived | the freshly located `WebElement` | | `elementToBeClickable(WebElement)` | no — it reuses the reference you passed | also caught and treated as "not yet", but nothing ever re-finds, so the wait spins out to `TimeoutException` | the same reference you passed in | The `WebElement` overload is the one that surprises people: handing it a reference captured before a re-render does not make the wait recover, it makes the wait fail slowly. ## A cinema showtime picker, and the shape of the failure Picture a screening-times page. Pick a date and the grid of showtime buttons is already in the markup and already enabled, but a translucent "checking seat availability" panel is drawn over the whole grid while the seat counts load. Every showtime button in that grid is **displayed** (it has a box, it is not `display: none`) and **enabled** (no `disabled` attribute). So: - `elementToBeClickable(By.cssSelector(".showtime[data-time='19:45']"))` is satisfied on its very first poll. - `until` returns immediately, typically in single-digit milliseconds, spending none of the timeout you configured. - The next line calls `click()`, the browser performs its own obstruction check as part of the click command, finds the panel over the button's centre, and the command fails with `ElementClickInterceptedException`. Nothing malfunctioned. The condition answered the question it was asked, truthfully; the question just was not the one the test needed answered. ## Using the condition for what it is - Treat it as a **readiness signal about one element's own state**, exactly equivalent to asserting `isDisplayed() && isEnabled()` in a loop. - It is genuinely the right tool when the thing you are waiting on **is** the element's own state — a submit button that starts disabled until a date is chosen, or a node that is rendered hidden and then revealed. - It is the wrong tool when the thing standing between you and the click is **something else on the page**. No amount of timeout makes a predicate check a property it never reads. - Because the condition returns the `WebElement` rather than a boolean, it is tempting to chain `wait.until(...).click()` on one line. That reads well and hides the fact that two independent operations, with a gap between them, are happening. The single sentence worth memorising: `elementToBeClickable` is `isDisplayed()` plus `isEnabled()`, and the word "clickable" in its name is aspiration, not contract.

  • How does the elementToBeClickable(WebElement) overload behave differently from the By overload?
    The `By` overload calls `findElement` on every poll, so a re-render is survived: the lookup exception is treated as “not yet” and the next poll finds the replacement. The `WebElement` overload runs the same `isDisplayed()`/`isEnabled()` pair against the reference you already hold. A stale reference is also swallowed as “not yet”, but nothing ever re-finds, so the wait simply spins until `TimeoutException`.
  • If the condition never checks obstruction, what does raise the error when something covers the button?
    The browser does, as part of executing the click command. When the target is obscured the remote end returns an `element click intercepted` error and the Java binding surfaces it as `ElementClickInterceptedException`, a subclass of `ElementNotInteractableException`. The check lives in the command, not in the wait, which is why a passing wait cannot predict it.
  • Does isEnabled() mean anything for an element that is not a form control?
    Very little. `isEnabled()` reports the inverse of a control's disabled state, and for an element that cannot be disabled it is simply `true`. A `<div>` or `<a>` styled as a showtime button therefore passes that half of `elementToBeClickable` unconditionally, leaving `isDisplayed()` as the only check with any content.

saying these in an interview costs you the question

  • Says elementToBeClickable guarantees the click will succeed
  • Thinks the condition inspects overlays, z-index or stacking order
  • Believes it waits for animations or CSS transitions to finish
  • Assumes displayed and enabled means reachable by a pointer
  • Claims a passing wait makes the following line safe