31

我们如何比较两个 HTML 元素是否相同?

我试过这个东西,但没有运气

<div class="a"> Hi this is sachin tendulkar </div>
<div class="a"> Hi this is sachin tendulkar </div>

然后在按钮单击时,我调用一个函数 check()

var divs = $(".a");
alert(divs.length);    // Shows 2 here which is correct
if (divs.get(0) == divs.get(1)) alert("Same");

但这不起作用。两个 div 中的一切都是一样的。除此之外,我们如何比较两个 HTML 元素是否完全相同。包括它们的innerHTML、className、Id 和它们的属性。

这是可行的吗?

实际上,我有两个 HTML 文档,我想从它们中删除相同的内容所以两个元素可以有相同的 id。

PS:在克劳德的宝贵意见后更新。如果我们将两个元素作为字符串进行比较,我们将不会得到匹配,因为它们的属性顺序可能会有所不同所以唯一的选择是遍历每个子属性并进行匹配。我仍然必须弄清楚完全可行的实施策略。

4

4 回答 4

55

您可以使用:

element1.isEqualNode(element2);

在您的具体示例中:

var divs = $(".a");
if ( divs.get(0).isEqualNode(divs.get(1)) ) alert("Same");

DOM Level 3 Core Spec包含所有细节。本质上,这将返回 true 的两个节点具有匹配的属性、后代和后代的属性。

有一个类似的 .isSameNode() 仅当两个元素是同一个节点时才返回 true。在您的示例中,这些不是相同的节点,但它们是相等的节点。

于 2013-10-13T06:37:24.263 回答
33

更新

请参阅Keen 的回答以及ccproj 对密切相关问题的回答。有isEqualNode这个,但它比较classstyle属性作为文本,所以同一组类或不同顺序的同一组样式属性会使其认为节点不等效。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;
}

活生生的例子:

于 2012-05-21T05:16:30.833 回答
1

为什么不做简单的方法呢?

<div id="div1"><div class="a"> Hi this is sachin tendulkar </div></div>
<div id="div2"><div class="a"> Hi this is sachin tendulkar </div></div>

if($('#div1').html() == $('#div2').html())
    alert('div1 & div2 are the same');        
else
    alert('div1 & div2 are different');

http://jsfiddle.net/5Zwy8/1/

于 2012-05-21T05:21:14.677 回答
0

这是怎么编码的?

var d1 = document.createElement("div");
d1.appendChild(divs.get(0));
var d2 = document.createElement("div");
d2.appendChild(divs.get(1));
if (d1.innerHTML == d2.innerHTML) ?
于 2012-05-21T05:22:06.890 回答