更新
请参阅Keen 的回答以及ccproj 对密切相关问题的回答。有isEqualNode
这个,但它比较class
和style
属性作为文本,所以同一组类或不同顺序的同一组样式属性会使其认为节点不等效。ccprog 的答案可以解决这个问题。
原始答案
(请参阅下面的完整的、大部分未经测试的、当然未经重构的现成解决方案。但首先,它的点点滴滴。)
比较它们innerHTML
很容易:
if (divs[0].innerHTML === divs[1].innerHTML)
// or if you prefer using jQuery
if (divs.html() === $(divs[1]).html()) // The first one will just be the HTML from div 0
...尽管根据您的标准,您必须问自己这两个元素是否等效:
<div><span class="foo" data-x="bar">x</span></div>
<div><span data-x="bar" class="foo">x</span></div>
...因为它们innerHTML
会有所不同(至少在 Chrome 上,我怀疑在大多数(如果不是所有)浏览器上)。(更多内容如下。)
然后你需要比较它们的所有属性。据我所知,jQuery 没有提供枚举属性的方法,但 DOM 提供了:
function getAttributeNames(node) {
var index, rv, attrs;
rv = [];
attrs = node.attributes;
for (index = 0; index < attrs.length; ++index) {
rv.push(attrs[index].nodeName);
}
rv.sort();
return rv;
}
然后
var names = [getAttributeNames(div[0]), getAttributeNames(div[1])];
if (names[0].length === names[1].length) {
// Same number, loop through and compare names and values
...
}
请注意,通过对上面的数组进行排序,我假设它们的属性顺序在您对“等效”的定义中并不重要。我希望是这样,因为它似乎没有被保留,因为我在运行这个测试时从不同的浏览器得到不同的结果。既然如此,我们就必须回到innerHTML
问题上来,因为如果元素本身的属性顺序不重要,那么推测后代元素上的属性顺序应该不重要。如果是这种情况,您将需要一个递归函数来根据您对等价物的定义检查后代,并且根本不使用innerHTML
。
然后是这个后续问题引起的关注:如果元素具有不同但等效的style
属性怎么办?例如:
<div id="a" style="color: red; font-size: 28px">TEST A</div>
<div id="b" style="font-size: 28px; color: red">TEST B</div>
我的回答通过遍历元素style
对象的内容来解决它,如下所示:
const astyle = div[0].style;
const bstyle = div[1].style;
const rexDigitsOnly = /^\d+$/;
for (const key of Object.keys(astyle)) {
if (!rexDigitsOnly.test(key) && astyle[key] !== bstyle[key]) {
// Not equivalent, stop
}
}
// Equivalent
可悲的是,正如我在那个答案中所说:
请注意,如果(其中一个 hascolor: red
和另一个 has color: #ff0000
),至少在某些浏览器上,上述操作将失败,因为当样式属性使用字符串值时,通常您会以提供的方式获取值,而不是标准化。您可以使用getComputedStyle
来获取计算的(ish)值,但随后我们会遇到有关 CSS 适用性的问题:具有完全相同标记的两个元素可能具有不同的值,getComputedStyle
因为它们在 DOM 中的位置以及应用于它们的 CSS结果。并且getComputedStyle
不适用于不在文档中的节点,因此您不能仅克隆节点以解决该问题。
但是您应该能够将上面的部分组合在一起,以根据您的标准比较两个元素。
更多探索:
这个问题让我很感兴趣,所以我踢了一会儿,想出了以下问题。它大多未经测试,可以使用一些重构等,但它应该能让你大部分时间到达那里。我再次假设属性的顺序并不重要。以下假设即使文本中最细微的差异也很重要。
function getAttributeNames(node) {
var index, rv, attrs;
rv = [];
attrs = node.attributes;
for (index = 0; index < attrs.length; ++index) {
rv.push(attrs[index].nodeName);
}
rv.sort();
return rv;
}
function equivElms(elm1, elm2) {
var attrs1, attrs2, name, node1, node2;
// Compare attributes without order sensitivity
attrs1 = getAttributeNames(elm1);
attrs2 = getAttributeNames(elm2);
if (attrs1.join(",") !== attrs2.join(",")) {
display("Found nodes with different sets of attributes; not equiv");
return false;
}
// ...and values
// unless you want to compare DOM0 event handlers
// (onclick="...")
for (index = 0; index < attrs1.length; ++index) {
name = attrs1[index];
if (elm1.getAttribute(name) !== elm2.getAttribute(name)) {
display("Found nodes with mis-matched values for attribute '" + name + "'; not equiv");
return false;
}
}
// Walk the children
for (node1 = elm1.firstChild, node2 = elm2.firstChild;
node1 && node2;
node1 = node1.nextSibling, node2 = node2.nextSibling) {
if (node1.nodeType !== node2.nodeType) {
display("Found nodes of different types; not equiv");
return false;
}
if (node1.nodeType === 1) { // Element
if (!equivElms(node1, node2)) {
return false;
}
}
else if (node1.nodeValue !== node2.nodeValue) {
display("Found nodes with mis-matched nodeValues; not equiv");
return false;
}
}
if (node1 || node2) {
// One of the elements had more nodes than the other
display("Found more children of one element than the other; not equivalent");
return false;
}
// Seem the same
return true;
}
活生生的例子: