skip to content

In Cypress, what does Cypress.dom.isDetached($el) return, and what can it not do?

level: middleimportance: nice to knowfreq 24%

answer

  1. A boolean, not a wait
  2. The exact negation of isAttached
  3. Empty collections report as detached
  4. Connection, not visibility
  5. Action commands already run the same check

basics

~20 s

It returns a boolean, the exact negation of Cypress.dom.isAttached: true unless every node you pass is connected to a document with a live window. It is a snapshot read that never waits, never re-queries and never repairs anything.

solid answer

~40 s

`Cypress.dom.isDetached($el)` is a synchronous helper on the `Cypress.dom` namespace, defined as `!isAttached($el)`. `isAttached` flattens a jQuery collection to its nodes and returns `true` only when every node is connected to a document whose window is still active — so an **empty** collection reports as detached, and so does a node whose document has been replaced. It is a connection test, not a visibility test; `Cypress.dom.isHidden()` answers that. What it cannot do is act: there is no timeout, no polling, no re-query and no repair, so the answer is only true for the instant you asked. Every action command already runs the equivalent guard through `Cypress.ensure.isAttached`, which is why a hand-written check in a spec usually means the chain should simply query again.

code

javascript · 9 lines
javascript
cy.get('[data-cy=room-row]').first().then(($row) => {
  cy.log(`before booking: detached=${Cypress.dom.isDetached($row)}`)

  cy.get('[data-cy=book-room]').first().click()

  cy.then(() => {
    cy.log(`after booking: detached=${Cypress.dom.isDetached($row)}`)
  })
})

go deeper

for a junior

Be ready to say that it returns a boolean about whether an element is still in the page, and that it belongs in debugging rather than in the body of a test.

for a middle

Explain the definition: the negation of the attached check, which requires every node to be connected to a document with a live window, so empty collections come back as detached.

for a senior

Show that you know the runner already performs this check before every action, and can say why a hand-rolled version in a spec is usually a sign the chain should re-query.

for a principal

Have a view on inspection helpers in general: they make the runner legible, but a suite whose control flow depends on them has moved logic out of the framework and into every spec.

## What the helper returns `Cypress.dom.isDetached($el)` is a synchronous boolean helper on the `Cypress.dom` namespace. It is defined as the exact negation of `Cypress.dom.isAttached($el)`, and that function is the interesting half. It builds an array of nodes from whatever you passed — a jQuery collection is flattened to its elements, a bare node is used as-is — and returns `true` only when **every** node in that array is connected to a document whose window is still active. Three consequences follow from that definition, and they are what interviewers are usually after: - **An empty jQuery collection reports as detached.** With no nodes to check, `isAttached` returns `false`, so `isDetached` returns `true`. "Detached" and "never existed" are the same answer here. - **A node in a document whose window has gone reports as detached** even if the node is still connected to that document — the window check runs first. A subject held across a `cy.visit()` or a reload lands in this case. - **It is about connection, not visibility.** A node hidden by `display: none` is still attached; `Cypress.dom.isHidden()` is the helper for that question. ## What it cannot do This is a report, not a guard. It answers for the instant you call it and then it is finished: - **It does not retry or wait.** There is no timeout and no polling. A `false` you read at the top of a `.then()` callback can be wrong by the time the next line runs. - **It does not re-query.** It cannot tell you what replaced the node, only that the node you handed it is gone. Recovering means running a query again. - **It does not reattach anything.** There is no repair path back to a live element. - **It is not the thing that protects your test.** Every action command already runs the equivalent check through `Cypress.ensure.isAttached` and fails with a message that names the situation. A hand-written check adds no safety that the runner was not already providing. ## Where it earns its place Because the runner already checks, a bare `if (Cypress.dom.isDetached($row))` in a spec is usually a smell: the honest fix is to stop holding `$row` and query again. The helper is worth reaching for in narrower places: 1. **Debugging.** Dropping it into a `.then()` while you are working out whether a room row survived a booking turns a guess into a fact, and the answer costs nothing to obtain. 2. **A custom command that must branch.** A shared helper handed a jQuery subject from an unknown caller can use it to decide whether to work with what it was given or to re-query from a stable anchor, rather than throwing a message that names the wrong thing. 3. **Reading Cypress's own behaviour.** Knowing that the same predicate drives the built-in guard makes the runner's detachment errors legible rather than mysterious. The `Cypress.dom` documentation warns that dozens of methods on the namespace are undocumented and used internally by nearly every built-in command. `isDetached` and `isAttached` are two of the documented ones, but the warning is a fair description of the register: these are inspection tools for the moments when you need to see what the runner sees, not a supported way to steer a test. ## The same predicate, inside the runner The reason the helper is worth knowing even if you never call it is that the runner leans on the same test in several places, and knowing that explains behaviour that otherwise looks arbitrary: - **Before every action.** `Cypress.ensure.isAttached` runs as part of the actionability checks, and it is one of the checks `{ force: true }` skips — which is why forcing makes the detachment error go away without making the element reachable. - **Before most assertions.** An assertion on an element subject runs the same guard first, so asserting on a detached row fails with the detachment message rather than with a confusing comparison failure. - **Except for existence and length.** Those two are exempt, because "is it attached" is exactly what an existence assertion is asking. A `have.length` assertion goes further and filters detached elements out of the collection before counting. ## A rule of thumb If the answer to `Cypress.dom.isDetached($row)` would change what your test does, the test is already holding an element it should not be holding. Read it to learn something; do not build control flow on it. The helper is a window into the runner's own bookkeeping, and the honest use of that window is to confirm a diagnosis in a booking spec you are debugging — then to delete the call and fix the chain that made you reach for it.

  • What does Cypress.dom.isDetached return for an element inside a document whose window is gone?
    `true`. The attached check tests the node's document for an active window before it tests the node's connection, so a subject held across a reload or a `cy.visit()` reports as detached even though it is still connected to the old document.
  • Is Cypress.dom.isDetached a reasonable guard to put in front of an action?
    Rarely. The action command already runs the same check and fails with a message that names the situation, and the helper cannot wait or re-query, so a guard built on it only changes which error you see. Query again instead of branching on it.

saying these in an interview costs you the question

  • Thinks it waits or retries like a Cypress assertion
  • Expects it to reattach or replace the element
  • Confuses detached with hidden or invisible
  • Assumes an empty collection reports as attached
  • Builds test control flow on its return value