Search code examples
javascriptjqueryhtmldomtraversal

Is there a way to print closing elements in a dom traversal using jQuery?


I am trying to learn jQuery for a new job offering I got and found myself getting a little confused with the different traversal commands it offers.

I want to print the name tags of each element, their depth, and their closing tags in order and was curious if there was a way to do this.

I have it working for descending elements, and elements with no children, but I cannot get the ascending closing tags for the elements.

Here is the html

<html id = "test">
<head>
</head>
<body>
    <p> Hello World</p> 
    <div id = "div1"></div>
</body>
</html>

here is my jQuery Script

$(document).ready(function() {

    domWalk();

    function domWalk() {
        $("html").find("*").addBack().each(function() {
            console.log( $(this).parents().length +" <"+ this.nodeName + "> ");
            if($(this).children() .length == 0){
                console.log( $(this).parents().length +" </"+ this.nodeName + "> ");
            }

        });
    };

});

Expected results

0<HTML>
1<HEAD>
1</HEAD>
1<BODY>
2<P>
2</P>
2<DIV>
2</DIV>
2<SCRIPT>
2</SCRIPT>
1</BODY>
0</HTML>

Actual results

0<HTML>
1<HEAD>
1</HEAD>
1<BODY>
2<P>
2</P>
2<DIV>
2</DIV>
2<SCRIPT>
2</SCRIPT>

Solution

  • jQuery will not give you by itself a way to do that, JavaScript will. You just need to build a recursive function that iterates over the nodes with children, something like this:

    function getTree($node, level) {
      level = level || 0;
      return $node.toArray().reduce(function(array, item) {
        var $item = $(item);
        var $children = $item.children();
        array.push(level + '<' + item.nodeName + '>');
        if ($children.length) {
          Array.prototype.push.apply(array, getTree($children, level + 1));
        }
        array.push(level + '</' + item.nodeName + '>');
        return array;
      }, []);
    
    }
    
    console.log(getTree($('html')));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <html id="test">
    
    <head>
    </head>
    
    <body>
      <p> Hello World</p>
      <div id="div1"></div>
    </body>
    
    </html>

    You will see that in the previous snippet it will take the script tags and the style tags that are created behind the scenes, but I think that the result is what you are searching for.

    This is the same function using regular JavaScript (ES6):

    const getTree = (node, level = 0) =>
      Array.prototype.reduce.call(node, (array, item) => {
        array = [...array, `${level}<${item.nodeName}>`];
        (item.children.length && (array = [...array, ...(getTree(item.children, level + 1))]));
        array = [...array, `${level}</${item.nodeName}>`];
        return array;
      }, []);
    
    console.log(getTree(document.querySelectorAll('html')));
    <html id="test">
    
    <head>
    </head>
    
    <body>
      <p> Hello World</p>
      <div id="div1"></div>
    </body>
    
    </html>