Re-query elements that are found 'detached' from the DOM
See original GitHub issueCurrent status
Update 12/06/2022: This work as been complete and will be released in Cypress v12 today.
This is currently in progress. See https://github.com/cypress-io/cypress/issues/7306#issuecomment-1182059194, https://github.com/cypress-io/cypress/issues/7306#issuecomment-1182427284 and https://github.com/cypress-io/cypress/issues/7306#issuecomment-1188245931 for a brief description of the plan and updates on current status.
Current behavior:
Currently when Cypress queries an element that was passed from a parent element (say in .get()
, .click()
or .find()
), if the element at some point becomes detached from the DOM, we throw an error:
CypressError: cy.click() failed because this element is detached from the DOM.
<element>
Cypress requires elements be attached in the DOM to interact with them.
The previous command that ran was:
cy.get()
This DOM element likely became detached somewhere between the previous and current command.
Common situations why this happens:
- Your JS framework re-rendered asynchronously
- Your app code reacted to an event firing and removed the element
You typically need to re-query for the element or add 'guards' which delay Cypress from running new commands.
https://on.cypress.io/element-has-detached-from-dom
Desired behavior:
Really users just want to re-query the DOM for the previously found element and click on the new element. Because it’s more often than not that the framework underneath has created a completely new DOM element and inserted it into the DOM.
The proposal is to re-query the previously found element and continue the command with any newfound elements. Perhaps this could be an option passed to the command to allow this or not.
Reproducible Example
Provided in https://github.com/cypress-io/cypress/issues/9032
index.html
<form>
<select onchange="document.forms[0].submit()">
<option value="1">First</option>
</select>
<input />
</form>
spec.js
it('this will fail but should not', () => {
cy.visit('index.html')
cy.get("select").select("First")
// adding any wait time will make it pass
// cy.wait(0)
cy.get("input").type("Hallo")
})
Related issues
Issue Analytics
- State:
- Created 3 years ago
- Reactions:545
- Comments:315 (60 by maintainers)
Top GitHub Comments
@TomaszG A plugin, a wait, an additional check in a loop, an additional selector. All I can say is… yikes. Cypress is kinda shitting the bed here.
All solutions here are at best hacky workarounds. The Cypress spirit is to interact with UIs like an user would do. This issue is indeed very technical and obviously an user is not supposed to know if an element is render one or multiple times, so as the test writer.
IMO, This re-query should be done systematically, without option to enforce the Retry-ability core-concept of Cypress.
For now, my only not-100%-safe workaround is a cy.wait(xxx) before any risky selector. It goes against core-concepts of this project.