skip to content

State and Attribute Reads

Reading what an element is showing: visibility, enablement and selection, rendered text only, and the Selenium 4 split of the old attribute getter into a markup read and a live property read.

on this pageshow

explore

questions

5

In Selenium 4, how do WebElement.getDomAttribute and getDomProperty differ, and where does getAttribute fit?

level: middleimportance: must knowfreq 76%

answer

  1. One name, two stores
  2. Markup default versus live object
  3. What the server sent, what the user changed
  4. Two W3C commands, one bundled script
  5. getDomAttribute against getDomProperty

basics

~20 s

getDomAttribute reads the value the page's HTML declared. getDomProperty reads the element's live DOM property, so it sees what the user typed or ticked. getAttribute is the older combined call: it prefers the property and falls back to the attribute.

solid answer

~40 s

In Selenium 4, `getDomAttribute(name)` reads the value the markup declared and `getDomProperty(name)` reads the live property on the DOM object. On a route planner's capacity field whose markup says `value="40"` and into which a dispatcher typed `60`, the first returns `"40"` and the second `"60"`. Each maps to its own W3C WebDriver command. `getAttribute(name)` is the older combined call: the Java client runs a bundled script that returns the property when one exists and falls back to the attribute, so it answers neither question precisely. Boolean names are the trap - `getDomAttribute("checked")` is `"true"` or `null` and reflects markup only, while `getDomProperty("checked")` is `"true"` or `"false"`. For checked and disabled, prefer `isSelected()` and `isEnabled()`.

code

java · 16 lines
java
WebElement capacity = driver.findElement(By.id("van-capacity"));
capacity.clear();
capacity.sendKeys("60");

// Markup said value="40" and still does
String declared = capacity.getDomAttribute("value");

// Live DOM property: what the dispatcher sees now
String current = capacity.getDomProperty("value");

WebElement delivered = driver.findElement(By.cssSelector("#stop-14 .delivered"));
delivered.click();

String markupChecked = delivered.getDomAttribute("checked"); // null
String liveChecked = delivered.getDomProperty("checked");    // "true"
boolean ticked = delivered.isSelected();                     // true

go deeper

for a junior

Recall that an element has both a value written into the HTML and a live value the browser keeps, and that Selenium 4 offers a separate read for each of them.

for a middle

Be ready to explain which call maps to which store, what each returns for a checkbox ticked after the page loaded, and why the older combined call can hand back either one.

for a senior

An interviewer expects you to spot the silent failure: a read of the markup attribute on a field the user has typed into looks correct on an empty form and reports the wrong value on a filled one.

for a principal

Own the convention across the suite: decide which read each kind of name gets, and be able to justify keeping or banning the older combined call given that it hides which store the author meant.

## Two stores of state behind one name Every element on a page carries two parallel stores of state, and Selenium 4 gives you a separate read for each one. - The **attribute** is what the served markup declared. On a courier route planner's vehicle form, `<input id="van-capacity" value="40">` has a `value` attribute of `40`, and that string does not change when a dispatcher types over it. - The **property** is the live field on the DOM object the browser built from that markup. Once the dispatcher types `60`, the element's `value` property is `60` while the attribute is still `40`. Selenium 4 exposes the first as `WebElement.getDomAttribute(String)` and the second as `WebElement.getDomProperty(String)`. In the Java binding both return a `String`, and both return `null` when there is nothing to read. ## What each call sends to the browser The two new calls are thin wrappers over two W3C WebDriver commands. The old call is not a command at all. | call | what it reads | wire command | result for `value="40"` after typing `60` | |---|---|---|---| | `getDomAttribute("value")` | the markup attribute | `GET /session/{id}/element/{id}/attribute/value` | `"40"` | | `getDomProperty("value")` | the live DOM property | `GET /session/{id}/element/{id}/property/value` | `"60"` | | `getAttribute("value")` | the property, attribute as fallback | `POST /session/{id}/execute/sync` running the `getAttribute.js` atom | `"60"` | The third row is the one that surprises people. In Selenium 4 the Java client maps `getAttribute` onto the execute-script command and ships the source of a bundled script — the `getAttribute.js` atom — as the body of every call. That script asks for the property first and falls back to the attribute only when the property is absent or is an object. So `getAttribute` is neither of the two clean reads; it is a heuristic that guesses which store you meant. ## The boolean-attribute trap Names such as `checked`, `disabled`, `selected`, `readonly`, `required` and `multiple` are **boolean attributes**: in HTML their mere presence is the signal. The three calls disagree about them. - `getDomAttribute("checked")` returns the string `"true"` when the attribute is present in the markup and `null` when it is not. It never returns `"false"`. - `getDomProperty("checked")` returns `"true"` or `"false"`, because the DOM property really is a boolean and the Java binding turns the value into a string. - `getAttribute("checked")` special-cases selectable elements and reports the element's *current* selectedness as `"true"` or `null`. On a route planner's "Delivered" checkbox that the test has just clicked, `getDomAttribute("checked")` is `null`, `getDomProperty("checked")` is `"true"`, and `getAttribute("checked")` is `"true"`. A read written against the markup attribute quietly reports the stop as undelivered. For exactly these names Selenium has dedicated predicates that sidestep the question: `isSelected()` for checkboxes, radios and options, and `isEnabled()` for form controls. Each returns a real `boolean` from its own W3C command, and reaching for them is what an interviewer wants to see. ## The names `getAttribute` quietly rewrites The old call also carries a compatibility layer that the Selenium 4 pair deliberately does not: - `getAttribute("class")` returns the `className` property, while `getDomProperty("class")` returns `null` — the property is spelled `className`, so the exact name matters. - `getAttribute("readonly")` returns the `readOnly` property under the same aliasing. - `getAttribute("style")` is flattened into the style declaration's text rather than the raw markup string. - `getAttribute("href")` on an anchor gives the browser's resolved absolute URL, whereas `getDomAttribute("href")` gives the raw `/routes/14` the page shipped. That layer exists because people confuse attributes with properties — which is the confusion the explicit pair was added to end. ## What all three reads have in common - Each is one command per call. Reading six names off a stop row is six round trips, and the older call additionally ships the atom's source in every request body. - Each performs the freshness check every `WebElement` call performs, so a read against an element the board has re-rendered raises `StaleElementReferenceException` rather than returning a stale string. - Each returns `null` for "nothing there", which is not the same as `""`. An input whose markup declares no `value` gives `null` from `getDomAttribute("value")` and `""` from `getDomProperty("value")`, because the DOM property of an empty field is the empty string. ## Choosing between them 1. **Reading what the user or the app just did** — a typed van capacity, a ticked stop, a button the page disabled — read the property, or better, the dedicated predicate. 2. **Reading what the server rendered** — a `data-stop-id`, an authored `href`, the `value` default the page shipped with — read the attribute. 3. **Writing anything new** — use the explicit pair. `getAttribute` still exists in Selenium 4 and is not deprecated, but every remaining call site leaves the next reader unable to tell which store was meant.

  • Why does getAttribute("class") work while getDomProperty("class") returns null?
    The DOM property is spelled `className`, so asking for the property called `class` finds nothing and Selenium returns `null`. The older `getAttribute` carries an alias table that rewrites `class` to `className` and `readonly` to `readOnly`, precisely because people confuse the two stores. With the Selenium 4 pair the name has to be exact: read `getDomAttribute("class")` for the markup or `getDomProperty("className")` for the property.
  • What does getDomProperty return when the property is not a string, such as a checkbox's checked flag?
    The Java binding turns the value into a `String`, so a boolean property comes back as `"true"` or `"false"` rather than as a `boolean`, and an unset property comes back as `null`. That is why comparing the result of `getDomProperty("checked")` against `"false"` is a legitimate read but a clumsy one - `isSelected()` returns a real `boolean` from its own command and cannot be compared against the wrong string.
  • When would you deliberately read the markup attribute rather than the live property?
    When the value you care about is what the server rendered: a `data-stop-id` the app never rewrites, the `value` default a form shipped with, or an `href` exactly as authored. Those either have no property at all or have a property the browser has normalised, so `getDomAttribute` is the faithful read and the property would answer a different question.

The attribute is the printed manifest the courier was handed at the depot; the property is the clipboard they have been scribbling on all morning. Reading the wrong one tells you the plan instead of the round.

saying these in an interview costs you the question

  • Says getDomAttribute returns whatever the user just typed
  • Treats getAttribute as identical to the DOM's own getAttribute call
  • Expects getDomAttribute on checked to return the string false
  • Reads the checked attribute instead of calling isSelected
  • Believes getAttribute was removed from Selenium 4
open as a page

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

level: seniorimportance: must knowfreq 61%

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.

open as a page

How would you move a large Selenium 4 suite off getAttribute onto getDomAttribute and getDomProperty?

level: principalimportance: must knowfreq 52%

basics

~20 s

Treat it as a semantic audit, not a rename. Group call sites by the name being read, move markup-only names first, turn boolean flags into isSelected and isEnabled, change live-value reads last, then ban the old call.

open as a page

In Selenium, why can WebElement.getText() return an empty string for an element whose markup has text?

level: middleimportance: should knowfreq 67%

basics

~20 s

Because it returns rendered text, not markup text. Anything the browser did not draw contributes nothing, so an element hidden by display none or visibility hidden yields an empty string even though its characters are in the page.

open as a page

What does Selenium's WebElement.getAriaRole() return, and how does it differ from reading the role attribute?

level: seniorimportance: nice to knowfreq 21%

basics

~20 s

It returns the WAI-ARIA role the browser computed for the element, including the implicit role of the tag. Reading the role attribute returns only the literal token the markup declared, or nothing when the markup declared none.

open as a page