Last active
November 2, 2024 00:11
-
-
Save adamscybot/b76a751ed5da6daaed33065d2832add8 to your computer and use it in GitHub Desktop.
Cypress example to check if a set of elements changed in a meaningful way via `isEqualNode`. Useful for detecting a change in a list of elements (positionally) according to each of their defining characteristics. Avoids detecting recreated/change elements that are really the same (e.g. ignore attr order, like for like replacements)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<body> | |
<div>initial element</div> | |
<button onclick="change()">Change element</button> | |
<script> | |
function change() { | |
// WILL NOT be detected as a change | |
setTimeout(() => { | |
const div1 = document.querySelector('div') | |
const div2 = document.createElement('div') | |
div2.innerText = 'initial element' | |
div1.replaceWith(div2) | |
}, 1000) | |
// WILL be detected as a change | |
setTimeout(() => { | |
const div1 = document.querySelector('div') | |
const div2 = document.createElement('div') | |
div2.innerText = 'changed element' | |
div1.replaceWith(div2) | |
}, 2000) | |
} | |
</script> | |
</body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This method of detecting changes in a list of elements is a sledgehammer and probably memory intense if your DOM is already memory intense. | |
// But leaning on the semantics of the web platforms `isEqualNode` is useful if those semantics are what you are after. | |
// | |
// The semantics are much closer to a "meaningful change" than comparing element references. But dont go all the way to | |
// a "user visible change" guarantee since that depends if things like certain possible attribute changes amount | |
// to a visible change in your app. | |
// | |
// It differs from the usual way of detecting changes (which should remain the usual, this gist is for edge cases). The | |
// usual way being targetted comparison of some individual aspects of the elements, e.g. visible text. The difference is | |
// that this is a good middle ground for detecting "any characteristic change" of an element in cases where perhaps you need | |
// something less brittle or dont even know or care what those changes might be for some reason (e.g. its external arbitrary content). | |
// | |
// It differs from comparing via crude `innerHTML` comparisons, since `isEqualNode` ignores things like attribute order. | |
// | |
// See https://developer.mozilla.org/en-US/docs/Web/API/Node/isEqualNode. | |
// Get initial elements of the list. Cloning them ensures we snapshot them to avoid breaking cases where the node is not replaced | |
// but mutated. We want that to be subject to the semantics of `isEqualNode` too and to ensure that we cant accidentally compare an element | |
// reference to itself. | |
cy.get('div').invoke('clone').as('initial') | |
// Some action that causes a change in the list | |
cy.get('button').click() | |
// Get new elements of the list | |
cy.get('div').as('final') | |
cy.get('@initial').then((initial) => { | |
cy.get('@final').should('satisfy', (final) => { | |
// If the list is different length we alrady know theres a difference. | |
if (final.length !== initial.length) return true | |
// Otherwise, at least one node needs to have changed in a meaningful way, for each position in the list. | |
return final | |
.toArray() | |
.some((el, index) => !el.isEqualNode(initial.get(index))) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment