Comparing nodes

I thought I wanted to compare the DOM trees for a couple of nodes. I ended up putting this together:

function normalizeString(s) {
  return s.replaceAll(/\s+/g, ' ').trim();
}

function compareAttributes(a1, a2) {
  a1 = a1 || [];
  a2 = a2 || [];
  let tested = {};
  for (const attr of a1) {
    tested[attr.name] = true;
    if (attr.value !== a2[attr.name].value) {
      return `${attr.name}: ${attr.value} !== ${a2[attr.name].value}`;
    }
  }
  for (const attr of a2) {
    if (tested[attr.name]) {
      continue;
    }
    if (attr.value !== a1[attr.name].value) {
      return `${attr.name}: ${a1[attr.name].value} !== ${attr.value}`;
    }
  }
  return '';
}

function compareNodes(n1, n2, path) {
  if (n1.isEqualNode(n2)) {
    return '';
  }
  if (n1.nodeType !== n2.nodeType) {
    return `${path} noteType: ${n1.nodeType} !== ${n2.nodeType}`;
  }
  if (n1.nodeType === Node.TEXT_NODE) {
    const t1 = normalizeString(n1.textContent);
    const t2 = normalizeString(n2.textContent);
    if (t1 !== t2) {
      return `${path} text: <<${t1}>> !== <<${t2}>>`;
    }
  }
  if (n1.tagName !== n2.tagName) {
    return `${path} tag: ${n1.tagName} !== ${n2.tagName}`;
  }
  if (n1.className !== n2.className) {
    return `${path} class: ${n1.className} !== ${n2.className}`;
  }
  const attrDiff = compareAttributes(n1.attributes, n2.attributes);
  if (attrDiff) {
    return `${path} ${attrDiff}`;
  }
  if (n1.childNodes.length !== n2.childNodes.length) {
    return `${path} childNodes.length: ${n1.childNodes.length} !== ${n2.childNodes.length}`;
  }
  for (let i = 0; i < n1.childNodes.length; i++) {
    let result = compareNodes(n1.childNodes[i], n2.childNodes[i], `${path}<${n1.tagName}>.childNodes[${i}]`);
    if (result) {
      return result;
    }
  }
  return '';
}

How would you have done it? (I’d be unsurprised if this has some bugs.)

After writing this though, I realized that comparing the HTML from n1.getHTML() vs n2.getHTML() was better for my purposes. (I don’t remember using getHTML() before; from that MDN link, it sounds like as I’m using it here, it’s equivalent to innerHTML.)

By the way, here’s what Gemini came up with in a few seconds in response to the prompt “could you write javascript to compare two dom nodes for equality?”

function compareDOMNodes(node1, node2) {
  // Check if nodes are the same element type
  if (node1.nodeName !== node2.nodeName) {
    return false;
  }

  // Check if attributes are equal
  if (node1.attributes.length !== node2.attributes.length) {
    return false;
  }
  for (let i = 0; i < node1.attributes.length; i++) {
    const attr1 = node1.attributes[i];
    const attr2 = node2.attributes[i];
    if (attr1.nodeName !== attr2.nodeName || attr1.nodeValue !== attr2.nodeValue) {
      return false;
    }
  }

  // Check if child nodes are equal
  if (node1.childNodes.length !== node2.childNodes.length) {
    return false;
  }
  for (let i = 0; i < node1.childNodes.length; i++) {
    if (!compareDOMNodes(node1.childNodes[i], node2.childNodes[i])) {
      return false;
    }
  }

  // Nodes are equal
  return true;
}

I like mine a bit better, but to be fair to Gemini, I also didn’t play with the prompt to refine it.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *