admin管理员组

文章数量:1352803

So I am trying create a function, that will console.log the entire DOM tree of the HTML page. It is supposed to output the tag name of each HTML element in the order they appear - children before siblings + attribute name.

I have this working piece of code:

"use strict";

document.addEventListener("DOMContentLoaded", traverse);

function traverse() {
    let elm = document.documentElement;
    displayInfo(elm);
};

function displayInfo(elm){
    //console.log(elm) 
    /* if (elm.childElementCount>0) {
        let children = Array.from(elm.children);
        console.log(children);
        children.forEach( displayInfo );
    }; */
    if (elm.hasAttributes) {
        //console.log(elm.attributes[0]);
    }; 
    var c = elm.childNodes;
    let i;
    for (i = 0; i < c.length; i++) {
        console.log(c[i].nodeName);
        if (c[i].childElementCount>0) {
            //console.log(c[i].childElementCount);
            if (c[i].hasAttributes) {
                //console.log(c[i].attributes[0]);
            };
            let cc = c[i].children;
            let ii;
            for (ii=0; ii < cc.length; ii++) {
                console.log(cc[ii].nodeName);
                if (cc[ii].hasAttributes) {
                    //console.log(cc[ii].attributes[0]);
                };
                if (cc[ii].childElementCount>0) {
                    //console.log(cc[ii].childElementCount);
                    let ccc = cc[ii].children;
                    let iii;
                    for (iii=0; iii < ccc.length; iii++) {
                        console.log(ccc[iii].nodeName);
                        if (ccc[iii].hasAttributes) {
                            //console.log(ccc[iii].attributes[0]);
                        };
                        if (ccc[iii].childElementCount>0) {
                            //console.log(ccc[iii].childElementCount);
                            let cccc = ccc[iii].children;
                            let iiii;
                            for (iiii=0; iiii < cccc.length; iiii++) {
                                console.log(cccc[iiii].nodeName);
                                if (cccc[iiii].hasAttributes) {
                                    //console.log(cccc[iiii].attributes[0]);
                                };
                                if (cccc[iiii].childElementCount>0) {
                                    console.log(cccc[iiii].childElementCount)
                                }
                            }
                        }
                    }
                }
            }
        }
    }
 };

The problem is, I am horribly repeating myself, plus I am manually setting how "deep" it will traverse. Is there a way to declare only one function that traverse?

PS: I know it's horrible to look at, I was just making sure the function would "work" all the way.

So I am trying create a function, that will console.log the entire DOM tree of the HTML page. It is supposed to output the tag name of each HTML element in the order they appear - children before siblings + attribute name.

I have this working piece of code:

"use strict";

document.addEventListener("DOMContentLoaded", traverse);

function traverse() {
    let elm = document.documentElement;
    displayInfo(elm);
};

function displayInfo(elm){
    //console.log(elm) 
    /* if (elm.childElementCount>0) {
        let children = Array.from(elm.children);
        console.log(children);
        children.forEach( displayInfo );
    }; */
    if (elm.hasAttributes) {
        //console.log(elm.attributes[0]);
    }; 
    var c = elm.childNodes;
    let i;
    for (i = 0; i < c.length; i++) {
        console.log(c[i].nodeName);
        if (c[i].childElementCount>0) {
            //console.log(c[i].childElementCount);
            if (c[i].hasAttributes) {
                //console.log(c[i].attributes[0]);
            };
            let cc = c[i].children;
            let ii;
            for (ii=0; ii < cc.length; ii++) {
                console.log(cc[ii].nodeName);
                if (cc[ii].hasAttributes) {
                    //console.log(cc[ii].attributes[0]);
                };
                if (cc[ii].childElementCount>0) {
                    //console.log(cc[ii].childElementCount);
                    let ccc = cc[ii].children;
                    let iii;
                    for (iii=0; iii < ccc.length; iii++) {
                        console.log(ccc[iii].nodeName);
                        if (ccc[iii].hasAttributes) {
                            //console.log(ccc[iii].attributes[0]);
                        };
                        if (ccc[iii].childElementCount>0) {
                            //console.log(ccc[iii].childElementCount);
                            let cccc = ccc[iii].children;
                            let iiii;
                            for (iiii=0; iiii < cccc.length; iiii++) {
                                console.log(cccc[iiii].nodeName);
                                if (cccc[iiii].hasAttributes) {
                                    //console.log(cccc[iiii].attributes[0]);
                                };
                                if (cccc[iiii].childElementCount>0) {
                                    console.log(cccc[iiii].childElementCount)
                                }
                            }
                        }
                    }
                }
            }
        }
    }
 };

The problem is, I am horribly repeating myself, plus I am manually setting how "deep" it will traverse. Is there a way to declare only one function that traverse?

PS: I know it's horrible to look at, I was just making sure the function would "work" all the way.

Share Improve this question asked May 12, 2018 at 10:30 the_odorthe_odor 551 silver badge8 bronze badges 2
  • Rather than if (condition) { big block }, you can if (!condition) continue; to reduce indentation hell. – CertainPerformance Commented May 12, 2018 at 10:31
  • And how would that look like in the example? I am sorry I am still not entirely familiar with the if statements – the_odor Commented May 12, 2018 at 10:34
Add a ment  | 

3 Answers 3

Reset to default 4

There is an API just for this which is way more powerful than anything you could try to build yourself: TreeWalker.

var walker = document.createTreeWalker(
  document.documentElement,
  NodeFilter.SHOW_ELEMENT // only elements
);
while (walker.nextNode()) {
  let current = walker.currentNode;
  console.log(
    current.tagName,
    [...current.attributes].map(({value,name}) => `${name}=${value}`).join()
  );
}
<article>
  <div id="container">container content
    <span>one span</span>
    <span class="nest1">nest1 span<span class="nest2">nest2 span</span></span>
  </div>
</article>

Just wanted to add this simple log of all nodes in a document object element names id and classes included placed in a neat array.

console.log(document.getElementsByTagName('*'));

Note that hasAttributes is a function, not a property. element.hasAttributes will always be true.

Use recursion, like this:

function displayInfo(node) {
  console.log(node.nodeName);
  if (node.nodeType === 3) {
    console.log('Text node: ' + node.textContent);
    return;
  }
  if (node.hasAttributes()) {
    [...node.attributes].forEach(({ name, value }) => console.log(`${name}=${value}`));
  }
  node.childNodes.forEach(displayInfo);
}
displayInfo(document.documentElement);
<article>
<div id="container">container content
<span>one span</span>
<span class="nest1">nest1 span<span class="nest2">nest2 span</span></span>
</div>
</article>

The continue shortcut I mentioned would look like this, to start with:

for (i = 0; i < c.length; i++) {
  console.log(c[i].nodeName);
  if (c[i].childElementCount === 0) continue;
  //console.log(c[i].childElementCount);
  if (c[i].hasAttributes) {
    //console.log(c[i].attributes[0]);
  };
  let cc = c[i].children;

Avoid indentation when you can - you'll make your code a lot more readable.

本文标签: javascriptHow to consolelog entire HTML DOM tree in order with attribute namesStack Overflow