# Find elements with subelements
Imagine we have a list of elements. Some elements can have <span class="label">...</span>
inside. We want to filter the top level list to only have elements that have a label inside.
<ol id="list">
<li>first</li>
<li>second</li>
<li>
<p>third <span class="label">New!</span></p>
</li>
<li>
<p>fourth <span class="label">New!</span></p>
</li>
<li>
<p>fifth <span class="label">Advanced!</span></p>
</li>
</ol>
<style>
.label {
background-color: #8b8bee;
padding: 0.2rem 0.5rem;
border-radius: 0.5rem;
}
</style>
- first
- second
third New!
fourth New!
fifth Advanced!
// select all list elements
cy.get('#list li')
.should('have.length', 5)
// filter the list by inspecting the children elements
.then(($elements) => {
// we can use the Lodash _.filter method to iterate
// over the DOM elements and filter by a predicate
const filtered = Cypress._.filter($elements, (el) => {
return el.querySelector('.label')
})
return filtered
})
.should('have.length', 3)
// confirm one of the list items
.last()
.should('include.text', 'fifth')
.find('.label')
.should('have.text', 'Advanced!')
// we can perform the opposite: only take the elements
// WITHOUT child element with class "badge" inside
cy.get('#list li')
.then(($elements) =>
// Lodash _.reject method is the opposite of _.filter
Cypress._.reject($elements, (el) =>
el.querySelector('.label'),
),
)
.should('have.length', 2)
.first()
.should('have.text', 'first')
What if we want to use Cypress commands inside the element filter callback? Like cy.contains
? Let's find all LI
elements that have labels with the text "Advanced" in them.
cy.get('#list li')
.should('have.length', 5)
.then(($elements) => {
// all found LI elements
const filtered = []
Cypress._.each($elements, (li) => {
// to run a Cypress command against the DOM element
// wrap it inside Cypress chain
cy.wrap(li)
.contains('.label', 'Advanced')
.should(($label) => {
// there might NOT be such element
if ($label.length) {
// only if there is such label,
// put the original "li" element
// into the filtered list
filtered.push(li)
}
})
})
// yield the filtered list of DOM elements
// to the next Cypress assertion / command
cy.wrap(filtered)
})
.should('have.length', 1) // yields [li] array
.its(0) // yields the first LI DOM element from the array
.should('include.text', 'fifth') // confirm we found the right element
We can also use the jQuery :has
selector to find LI
elements with .label
elements inside.
cy.get('li:has(.label)')
.should('have.length', 3)
.last()
.contains('Advanced')
We can even use the :has
combined with :contains
to find all LI
elements with .label
elements that have the text "New" in them.
cy.get('li:has(.label:contains("New"))').should('have.length', 2)
Watch the video "jQuery :has and :contains Selectors in Cypress Tests (opens new window)".
# Find labels OR warnings
Imagine that some list elements have plain labels and some have warnings. Can we find the list elements with labels OR warnings using :has
selector?
<ol id="list">
<li>first</li>
<li>second</li>
<li>
<p>third <span class="badge label">New!</span></p>
</li>
<li>
<p>fourth <span class="badge label">New!</span></p>
</li>
<li>
<p>fifth <span class="badge warning">Deprecated</span></p>
</li>
</ol>
<style>
.badge {
padding: 0.2rem 0.5rem;
border-radius: 0.5rem;
}
.label {
background-color: #8b8bee;
}
.warning {
background-color: #f99650;
}
</style>
- first
- second
third New!
fourth New!
fifth Deprecated
// use jQuery CSS selector :has to find
// all list items with an element with class label or warning inside
cy.get('li:has(.label, .warning)').should('have.length', 3)
// alternative: split into two commands
// using the https://on.cypress.io/filter command
cy.get('li')
.should('have.length', 5)
.filter(':has(.label, .warning)')
.should('have.length', 3)
Watch the video Find Elements With Specific Child Elements Using Cypress jQuery :has Selector (opens new window).