Node.isEqualNode() does compare children. See the spec for the complete algorithm, if applied to elements:
A node A equals a node B if all of the following conditions are true:
- A and B's nodeType attribute value is identical.
- The following are also equal...:
- [Element:] Its namespace, namespace prefix, local name, and its number of attributes in its attribute list.
- If A is an element, each attribute in its attribute list has an attribute with the same namespace, local name, and value in B's attribute list.
- A and B have the same number of children.
- Each child of A equals the child of B at the identical index.
The problem remains the text comparison of the style
attribute values, and the same argument - functionally the same, but textually different - can be applied to the class
atribute.
A solution is to produce a clone that has a defined (alphabetical) order for the style properties and class names.
Element.style
returns an object both with string keys and numeric keys. The first are a list containing all CSS properties in existence, whether they are really set in the style attribute or not. That is a long list, and most entries are the empty string.
That is where the numeric keys help: the object contains array-like entries listing all the property names that are actually set. Converting them with Array.from()
to a real array makes it possible to only get the relevant parts and sort them.
Element.classList
is an equally Array-like list.
Both properties are considered read-only, so to write back, use the basic method to write to a named attribute.
$.fn.normalizeTree = function () {
return this.each(function () {
$(this).add("*", this).each(function () {
const sortedClass = Array.from(this.classList)
.sort()
.join(' ');
$(this).attr('class', sortedClass);
const sortedStyle = Array.from(this.style)
.sort()
.map(prop => `${prop}: ${this.style[prop]};`)
.join('');
$(this).attr('style', sortedStyle);
});
});
}
const rawNodes = $(".comparable");
if (rawNodes[0].isEqualNode(rawNodes[1])) {
$("#result1").text("equal")
}
const normalNodes = rawNodes.clone().normalizeTree();
if (normalNodes[0].isEqualNode(normalNodes[1])) {
$("#result2").text("equal")
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="comparable">
<p class="one two"><span style="color:rgb(0, 0, 0);font-family:sans-serif;font-size:15px">A text.</span></p>
</div>
<div class="comparable">
<p class="two one"><span style="font-size:15px;font-family: sans-serif;color:rgb(0, 0, 0);">A text.</span></p>
</div>
<p>These are <span id="result1">not equal</span> if raw.</p>
<p>These are <span id="result2">not equal</span> if normalized.</p>