JS function that outputs a DOM-tree by button click

Viewed 370

I need to write a js function that outputs a DOM-tree by button click. The tree should be output as an unnumbered list (ul) with attachments and it's needed to use the name of the element, i.e. head, body, p, div, etc., and the element id as the text output in the list item (of course if it is specified). I've tried to write it but I don't know how to make it work and what's wrong here

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body><div id="container1" style="background-color: cyan;">
      <h1 id="header1">Header</h1>
      <p id="paragraph1">Paragraph</p>
      <div id="container2" style="background-color: red;">
      </div>
    </div>
    <ul id="tree"></ul>
    <input type="text" id="formText">
    <br>
    <button id= "confirmButton" style="margin-top: 5px;">Build a DOM tree</button>
  </body>
</html>
function DOM_Tree(e) {
  for (let i = 0; i < document.body.childNodes.length - 1; i++) {
    if (document.body.childNodes[i].id != 'tree') {
      let ul = document.getElementById('tree');
      let li = document.createElement('li');
      let el = document.body.childNodes[i];
      let ul1 = document.createElement('ul');
      if (el.hasChildNodes()) {
        li.innerText = document.body.childNodes[i].id;
        ul.append(li);
        for (let j = 0; j < el.childNodes.length; j++) {
          if (el.childNodes[j].id != undefined) {
            let li1 =  document.createElement('li');
            li1.innerText = el.childNodes[j].id;
            ul1.append(li1);
          }
           let li1 =  document.createElement('li');
           li1.innerText = el.childNodes[j].id;
           ul1.append(li1);
        }
        ul.append(ul1);
      }
      else {
        if (document.body.childNodes[i].id != undefined) {
          li.innerText = document.body.childNodes[i].id;
          ul.append(li);
        }
       }
     }
  }
}
confirmButton.onclick = function() {
  DOM_Tree(document.body);
  alert('click');
}
2 Answers

Are you getting the error:

Uncaught TypeError: document.body.getElementById is not a function
    at DOM_Tree (<anonymous>:4:32)
    at HTMLButtonElement.confirmButton.onclick (<anonymous>:26:7)
DOM_Tree @ VM167:4
confirmButton.onclick @ VM167:26

You can check this error in the console output in your browser (in Chrome, press F12, or right click and 'inspect', you'll see the console output, with useful errors or debugging console logs:

enter image description here

You need document.getElementById not document.body.getElementById.

You're also adding two click events. Your first onclick="DOM_Tree(e)" I suspect didn't work because you meant onclick="DOM_Tree(event)" or onclick="DOM_Tree(this)". You might not even want the event (you don't really want to loop through the children of the button!) in which case onclick="DOM_Tree()" would work.

Your code had a number of logical flaws.

Here's a working solution:

function DOM_Tree(e, ul = document.getElementById('tree')) {
  for (let i = 0; i < e.childNodes.length - 1; i++) {
    if (e.childNodes[i].id != 'tree') {
      let li = document.createElement('li');
      let el = e.childNodes[i];
      if (e.childNodes[i].id != undefined) {
        li.innerText = e.childNodes[i].nodeName + ' ' + e.childNodes[i].id;
        ul.append(li);
      }
      let ul1 = document.createElement('ul');
      DOM_Tree(e.childNodes[i], ul1);
      ul.append(ul1);
    }
  }
}
confirmButton.onclick = function() {
  DOM_Tree(document.body);
  // alert('click');
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body><div id="container1" style="background-color: cyan;">
      <h1 id="header1">Header</h1>
      <p id="paragraph1">Paragraph</p>
      <div id="container2" style="background-color: red;">
      </div>
    </div>
    <ul id="tree"></ul>
    <input type="text" id="formText">
    <br>
    <button id= "confirmButton" style="margin-top: 5px;">Build a DOM tree</button>
  </body>
</html>

And here's all the changes I did


A high level explanation of what your code was doing:

  1. DOM_Tree got called with document.body as a parameter (but you didn't actually use that parameter anywhere)
  2. You iterate over all of the nodes in document.body
  3. If the node contains children, then you put that node's info into the DOM, then start iterating over the node's children. If it does not contain children, then you just add it's info to the DOM
  4. You pretty much do the exact same thing that you did in step 3, but for the grandchildren.

Hopefully this high level explanation helps show why it wasn't working. If a human was following those instructions, they wouldn't end up with the desired results either.

What I did was

  1. eliminate step 4 (the inner loop) - it's the exact same logic as step 3, and only needs to be done once
  2. I cleaned it up just a tad. In step 3, weather or not there's children, you're going to add that node's info to the DOM. So, I just move that outside the if, which eliminated the need for an else, and actually the if wasn't needed either, because if there were no children, then the loop wouldn't happen.
  3. I stopped hard coding in the usage of document.body. Instead, I referred to the "e" parameter you were passing in, which was initially set to document.body anyways.
  4. This allowed me to recursively call this function. Once I got to the point where I needed to process the children of document.body, I just called DOM_Tree again, passing in each child. Each child in turn would call DOM_Tree for each of its children. Thus, the whole DOM would get looked at, not just the first two levels like you had in your implementation.
  5. I made it take in an additional parameter - the node where we're currently inserting the information at. This is important so that the recursive DOM_Tree calls can insert the information it finds into the right spot in the DOM.
Related