如何比较两个 HTML 元素

新手上路,请多包涵

我们如何比较两个 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:根据克劳德的宝贵意见更新。如果我们将两个元素作为字符串进行比较,我们将无法获得匹配项,因为它们的属性顺序可能不同所以唯一的选择是遍历每个子属性并进行匹配。我仍然需要找出完全可行的实施策略。

原文由 Sachin Jain 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 583
2 个回答

更新

请参阅 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

可悲的是,正如我在那个答案中所说:

请注意,如果 (其中一个有 color: red 而另一个有 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;
}

现场示例:

原文由 T.J. Crowder 发布,翻译遵循 CC BY-SA 4.0 许可协议

您可以使用:

 element1.isEqualNode(element2);

在您的具体示例中:

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

DOM Level 3 核心规范 包含所有详细信息。本质上,如果两个节点具有匹配的属性、后代和后代的属性,则返回 true。

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

原文由 Keen 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题