0

I'm new to JavaScript and am trying to create a recursive function that checks if two DOM nodes are equivalent. This function seems to be returning true for everything and isn't checking the DOM the way I want it to for some reason. Only nodes 1 & 4 are equivalent.

var htmlStrings = ['<div id="one">Some<span>node <em>contents</em> for</span>comparison</div>', '<div id="two">Some<span>node contents for</span>comparison</div>', '<div id="one">Some<span>node <strong>contents</strong> for</span>comparison</div>', '<div id="four">Some<span>node <em>contents</em> for</span>comparison</div>'];

var div1 = document.createElement('div');
div1.innerHTML = htmlStrings[0];
document.body.appendChild(div1);

var div2 = document.createElement('div');
div2.innerHTML = htmlStrings[1];
document.body.appendChild(div2);

var div3 = document.createElement('div');
div3.innerHTML = htmlStrings[2];
document.body.appendChild(div3);

var div4 = document.createElement('div');
div4.innerHTML = htmlStrings[3];
document.body.appendChild(div4);


function nodeEquivalence(node1, node2) {
    var passed = false;


        if (node1.nodeType === node2.nodeType) {
            if ((node1.tagName === node2.tagName && node1.nodeValue === node2.nodeValue)) {
               passed = true;
            } 
        }

        node1 = node1.firstChild;
        node2 = node2.firstChild;
        while (node1 && node2) {
           nodeEquivalence(node1, node2);
            node1 = node1.nextSibling;
            node2 = node2.nextSibling;

        }

        return passed;

}


console.log(nodeEquivalence(div1, div2));
console.log(nodeEquivalence(div1, div4));
devdropper87
  • 4,025
  • 11
  • 44
  • 70
  • Please clear the title of your question a little bit. http://stackoverflow.com/help/how-to-ask. Thanks! –  Jul 11 '15 at 02:24

4 Answers4

1

You're passing strings, not DOM elements. You need to convert the HTML to DOM elements. There are many solutions described at

Creating a new DOM element from an HTML string using built-in DOM methods or prototype

So you can do:

var html1 = '<div id="one">Some<span>node <em>contents</em> for</span>comparison</div>';
var html2 = '<div id="four">Some<span>node <em>contents</em> for</span>comparison</div>';
var html3 = '<div id="one">Some<span>node <b>contents</b> for</span>comparison</div>';

var div1 = document.createElement('div');
div1.innerHTML = html1;
var div2 = document.createElement('div');
div2.innerHTML = html2;
var div3 = document.createElement('div');
div3.innerHTML = html3;

alert(nodeEquivalence(div1.firstChild, div2.firstChild));
alert(nodeEquivalence(div1.firstChild, div3.firstChild));


function nodeEquivalence (node1, node2) {
  var passed = true;

  function test(node1, node2) {
    if ((node1.nodeType === node2.nodeType) && (node1.tagName === node2.tagName || node1.nodeValue === node2.nodeValue) && (node1.childNodes.length === node2.childNodes.length)) {
      passed = true;
    } else {
      passed = false;
    }
  }

  node1 = node1.firstChild;
  node2 = node2.firstChild;
  while (passed && node1 && node2) {
    test(node1, node2);
    node1 = node1.nextSibling;
    node2 = node2.nextSibling;

  }
  //test(document.body);
  return passed;
};
Community
  • 1
  • 1
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Sorry, I reordered the code after testing, and didn't notice that you used a function assignment instead of a function declaration. I've fixed it now. – Barmar Jul 11 '15 at 15:31
  • Thanks. so the undefined issue is fixed with this code, but when I try it in chrome dev tools it's returning true for everything. only div1 and div4 are equal. updated code above. – devdropper87 Jul 11 '15 at 17:44
  • You're setting `passed = true` whenever both nodes have the same number of children. Then if the children don't match, you never set it to `false`. – Barmar Jul 11 '15 at 18:38
  • Your function isn't recursive. It just calls `test()` on the children of the given nodes, it never goes deeper to test their children. – Barmar Jul 11 '15 at 18:48
  • hmm but each time the childrens nodes get set to their first children (what's above the while loop) thus it goes down to test their children. in any case i think i'm off topic i'll start a new question for this latest issue :) thanks! – devdropper87 Jul 11 '15 at 21:22
  • But it doesn't go to the children's children. – Barmar Jul 11 '15 at 21:39
  • I'm not seeing that... when I call nodeEquivalence on the children, after the ifs, the current node is set to be the children, then nodeEquivalence gets called on the children again and then it repeats so that nodeEquivalence is called on the childs child. I adopted that part of the code from Douglas Crockford's walking the DOM so I thought that part should definitely work. This is a helpful exercise for me, so if I'm missing something please let me know: http://snipplr.com/view/19815/walking-the-dom/ – devdropper87 Jul 11 '15 at 21:44
  • You never call `nodeEquivalence` on the children. You just call `test`. – Barmar Jul 11 '15 at 21:47
  • Got it, realized the code wasn't updated... updating it. my bad. – devdropper87 Jul 11 '15 at 22:15
  • You don't do anything with the return value of the recursive call. – Barmar Jul 12 '15 at 23:19
0

Use document.createElement(*tagName*)

Here is some documentation.

For example, you'll want to create two elements, pass them both in and see if they're equivalent. And then you can base the same one in twice.

var newDiv = document.createElement("div");
var newSpan = document.createElement("span");
ChadF
  • 1,750
  • 10
  • 22
0

yes, instead of comparing 2 html elements, you are simply comparing 2 strings. And your node1, node2 will always be undefined.

by the way, this following has some nice examples as to how to compare 2 html elements.

How to compare two HTML elements

Community
  • 1
  • 1
dshun
  • 223
  • 2
  • 9
0

Most of the relevant part is innerHTML. Most of the information is in there. If the innerHTML of the two HTML nodes are the same, than nearly everything is the same. Than the tagName and for <input> tags the type attribute:

function nodeEquivalence(node1, node2) {
    var equal = false;
    if (node1.innerHTML === node2.innerHTML) {
        if (node1.tagName === node2.tagName) {
            if (node1.type === node2.type) {
                equal = true;
            }
        }
    }
    return equal;
}

I think, this function will catch nearly all cases.

Note that there is a little difference, if you access the node via id or class name:

var el = document.getElementById(id);
el.innerHTML

var el2 = document.getElementsByClassName(className);
el[0].innerHTML;

Also, you can compare by outerHTML. When you give each HTML node the same class name, you will have the exact same thing.

Example

Create a HTML element:

var div = document.createElement("div");
var text = document.createTextNode("some text");
div.appendChild(text);
document.body.appendChild(div);
  • Your nodeEquivalence function is too complicated. See, how I did it. InnerHTML is often used to set text in an tag. But this is not the correct way. InnerHTML is what the name says, "what is in HTML" (the whole HTML tree). To set text use textContent or createTextNode. The message "undefined" comes, because you didn't append it to document. I will update my answer to clear it up! –  Jul 11 '15 at 15:46
  • document.body.appendChild(el) is missing. –  Jul 11 '15 at 15:54
  • The function is correct! The innerHTML from your div1 has another id than the div4. Now you create a text node and in the node you put HTML. That is not the meaning. CreateTextNode is there to create plain text! http://jsfiddle.net/zh61e9qb/4/ –  Jul 11 '15 at 18:38