Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive function for going through unknown DOM

I am learning about js DOM and I want to make a recursive function that I could use to go through all nodes in any DOM. I made it, but I can not figure out why my first attempt is not working:

HTML

function mostrarNodosV2(node) {
  console.log(node.nodeName);
  if (node.firstElementChild != null) {
    node = node.firstElementChild;
    mostrarNodosV2(node);
  }

  if (node.nextElementSibling != null) {
    node = node.nextElementSibling;
    mostrarNodosV2(node);
  }

}

mostrarNodosV2(document);
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Exercise IV</title>
</head>

<body>
  <h1> Just a header</h1>
  <p>Nice paragraph</p>
  <ul>
    <li>Im just an element list on an unordered list</li>
  </ul>
</body>

</html>

The flow is next:

  1. document node.
  2. we repeat the function with his first child: head node.
  3. we repeat the function with his first child: meta node.
  4. because of 'meta' has no childs, we repeat the function with his next sibling: title node.
  5. because of 'title' has no childs or next sibling, we end the function where node=title, we should end the function where node = meta, and we should continue checking the next sibling of head: body node.

Instead of that, if you debug or check the console you will see that browser repeats the section:

if (node.nextElementSibling != null) {
    node = node.nextElementSibling;
    mostrarNodosV2 (node);
}

Where node = meta, and so we get two 'TITLE' printed on console. Then it goes as it should have gone, and we get the 'body' node. The same problem happens with the 'LI' element.

So, I do not want another solution, I just did it, I just want to know why I go back to that 'if', because I don't get it.

If you debug it on developer tools it would be easier to understand.

like image 501
Ornitier Avatar asked Jan 14 '16 17:01

Ornitier


2 Answers

The reason why your recursive function repeated nodes was because you reassigned node. Let's step through the function ourselves:

document -> has a child
  html -> has a child
    head -> has a child
      meta -> has no child, has a sibling
        title -> has no child or sibling
    head -> head has been overwritten with meta, which has a sibling
      title -> has no child or sibling
  html -> html has been overwritten with head, which has a sibling
    body -> has a child
      h1 -> has no child, has a sibling
        p -> has no child, has a sibling
          ul -> has a child
            li -> has no child or sibling
          ul -> ul has been overwritten with li, which has no sibling
    body -> body has been overwritten with h1, which has a sibling
      ...

So now you understand why it's bad to overwrite the function argument.

If you would like a more robust approach, here is how I would have written a recursive DOM traversal function:

function mostrarNodosV2(node) {
  if (node == null) {
    return;
  }

  console.log(node.nodeName);

  mostrarNodosV2(node.firstElementChild);
  mostrarNodosV2(node.nextElementSibling);
}

mostrarNodosV2(document);

The only difference here is I'm checking the node validity one recursion deeper than you were for each node, which reduces the verbosity of your approach.

like image 144
Patrick Roberts Avatar answered Oct 01 '22 14:10

Patrick Roberts


you are re-assigning node variable, try this:

function mostrarNodosV2(node) {
    console.log(node.nodeName);
    if (node.firstElementChild != null) {
        var child = node.firstElementChild;
        mostrarNodosV2(child);
    }
    if (node.nextElementSibling != null) {
        var sibling = node.nextElementSibling;
        mostrarNodosV2(sibling);
    }
}
mostrarNodosV2(document);
like image 22
madox2 Avatar answered Oct 01 '22 12:10

madox2