skip to content

In Selenium, what does WebElement.isDisplayed() actually compute, and why is it not normatively specified?

level: seniorimportance: must knowfreq 61%

answer

  1. Not a real driver command
  2. A script walks the tree
  3. Folds several CSS conditions into one boolean
  4. Nothing checks what is painted on top
  5. The specification puts it in an appendix

basics

~20 s

It runs a bundled Selenium script that approximates visibility by walking the element tree: display none, visibility hidden, zero opacity, zero size and unscrollable overflow all make it false. The W3C specification only recommends this approach in an appendix.

solid answer

~40 s

In Selenium 4 `isDisplayed()` is not a driver primitive like `isEnabled()` or `isSelected()`. The Java client maps it onto the execute-script command and posts a bundled script, the `isDisplayed.js` atom wrapping Selenium's `bot.dom.isShown`, which walks the element tree. It answers `false` for `display: none` on the element or an ancestor, `visibility: hidden` or `collapse`, computed `opacity: 0`, a hidden input, a zero-sized box with no sized child, and overflow that cannot be scrolled into view. It never checks whether something is painted on top, whether the element is inside the current viewport, or whether it would accept input. The W3C WebDriver specification deliberately defines no visibility primitive and puts this approach in a non-normative appendix, calling it a simplified approximation.

code

java · 12 lines
java
WebElement banner = driver.findElement(By.id("route-conflict-banner"));

// One script execution per call, run inside the page
boolean shown = banner.isDisplayed();

// The computed values the atom folds into that single boolean
String display = banner.getCssValue("display");
String visibility = banner.getCssValue("visibility");
String opacity = banner.getCssValue("opacity");

// With opacity "0", shown is false even though display is "block"
System.out.println(shown + " " + display + " " + visibility + " " + opacity);

go deeper

for a junior

Recall that the call returns a boolean for whether an element is shown, and that a hidden element is still in the page and can still be found by a locator.

for a middle

Explain the concrete rules: display none anywhere up the ancestors, visibility hidden, zero opacity and a zero-sized box all give false, and the answer comes from a script rather than a driver command.

for a senior

Demonstrate that you know where it lies. Be able to name the cases it gets wrong in production - occlusion, off-viewport content, transparent text - and say what you read instead when the distinction matters.

for a principal

Own the consequence for the suite: a boolean that folds several CSS conditions together gives no diagnosis when it is false, so decide what the team reads to explain a failure rather than merely detect one.

## A script in the page, not a driver primitive `WebElement.isDisplayed()` looks like a sibling of `isEnabled()` and `isSelected()`, but it is built differently. Those two are backed by real W3C WebDriver commands. Displayedness is not: in Selenium 4 the Java client maps the call onto the execute-script command and posts a bundled script — the `isDisplayed.js` atom, which wraps Selenium's own `bot.dom.isShown` function — to `POST /session/{id}/execute/sync`. The browser runs it against the element and returns a boolean. That single fact explains most of the method's behaviour. It is an **approximation computed by traversing the element tree**, not something the browser was asked directly. ## The rules the atom actually applies Walking a courier route planner's dispatch board, these are the conditions that make the answer `false`: - the element or any ancestor has computed `display: none`, or `content-visibility: hidden`; - the element has computed `visibility: hidden` or `visibility: collapse`; - the element's computed `opacity` is `0`; - it is an `<input type="hidden">`, or a `<noscript>` element; - its bounding box has zero width or height, and no child node with a positive box rescues it (a zero-sized wrapper with `overflow: hidden` stays hidden); - it overflows an ancestor in a way that cannot be scrolled into view; - it is inside a closed `<details>` element and is not that element's `<summary>`. Two special cases are worth remembering because they are asked: `<body>` is always reported as displayed, and an `<option>` or `<optgroup>` is displayed exactly when its enclosing `<select>` is, ignoring the select's opacity. ## What it deliberately does not check - **Occlusion.** Nothing in the traversal asks whether another element is painted on top. A stop row underneath the route planner's sticky dispatch header is still displayed. - **The viewport.** An element far below the fold is displayed, because the page can be scrolled to it; only overflow that cannot be scrolled into view counts as hidden. - **Contrast.** Text rendered in the background colour, or a `color: transparent` label, is displayed. - **Readiness for input.** A `true` answer says a box was drawn, not that anything will land on it. - **Time.** The result is a snapshot; the board can re-render the instant after the value comes back. ## Why the specification kept it out of the normative text The W3C WebDriver specification says plainly that it defines no primitive for element visibility, and puts displayedness in a **non-normative appendix**. The appendix acknowledges the feature matters, describes the approach as a "simplified approximation" that relies only on tree traversal and covers a subset of visibility checks, credits the approach to the Selenium project, and notes that it is typically exposed at `/session/{session id}/element/{element id}/displayed`. The reason is that real visibility depends on layout, compositing, clipping and paint order — properties no tree walk can settle. Rather than standardise a wrong answer, the specification recommends a known-imperfect one. Selenium 4 therefore does not rely on drivers implementing it and ships the atom itself, which is also why the answer is consistent across browsers: they are all running the same script. | condition on a stop row | `isDisplayed()` | |---|---| | ancestor has `display: none` | `false` | | `visibility: hidden` on the row | `false` | | computed `opacity: 0` | `false` | | zero height with no sized child | `false` | | scrolled below the fold, page can scroll to it | `true` | | fully covered by the sticky dispatch header | `true` | ## What the answer is worth in production - It is a genuine round trip and a script execution per call, and the atom's source travels in the request body each time. Calling it in a loop over 200 dispatch rows is 200 script executions. - Because it folds several CSS conditions into one boolean, a `false` tells you nothing about *why*. When you need the reason, read the pieces: `getCssValue("display")`, `getCssValue("visibility")` and `getCssValue("opacity")` return the computed values the atom folded together. - The honest sentence for an interview is that `isDisplayed()` answers "would this element have been drawn, ignoring anything on top of it" — useful, cheap to say, and not a proof that a courier could have seen it.

  • A stop row sits under a sticky dispatch header. What does isDisplayed() say, and why?
    It returns `true`. The visibility walk only inspects the element and its ancestors - display, visibility, opacity, box size and overflow - and never asks what is painted on top of the element's box. Occlusion by a sibling or a fixed header is invisible to it. That is the single most common reason a `true` answer disagrees with what a person looking at the dispatch board would say.
  • Is an element below the fold of a long route list displayed?
    Yes. Overflow that can be scrolled into view counts as visible; only overflow that cannot be reached by scrolling is treated as hidden. So a stop 40 rows down the list reports `true` even though nobody has scrolled to it. If you need to know that something is inside the current viewport, `getRect()` gives you the element's document-relative box and you compare it yourself.
  • Why does isDisplayed() give the same answer across Chrome, Firefox and Safari?
    Because they are all running the same code. Selenium 4 does not depend on each driver implementing displayedness; the client posts its own bundled script to the execute-script endpoint, so the rules come from Selenium rather than from the browser. The consistency is real, but it is consistency of an approximation, not of anything the browsers themselves computed.

saying these in an interview costs you the question

  • Says isDisplayed proves a person could see the element
  • Thinks it returns false for anything outside the viewport
  • Believes it detects an overlay covering the element
  • Calls it a normative W3C WebDriver command
  • Assumes each browser implements its own visibility rules