HTML DOM iteration and getting depth

How can I iterate over the HTML DOM and list all nodes with depth in javascript.

Example:

<div> <img src="foo.jpg"> <p> <span>bar</span> </p> </div> 

will result in

  • Div 0
  • img 1
  • p 1
  • gap 2
+8
source share
8 answers

Write a recursive function that tracks depth:

 function element_list(el,depth) { console.log(el+' '+depth); for(var i=0; i<el.children.length; i++) { element_list(el.children[i],depth+1); } } element_list(document,0); 

As CodeiSir points out , text nodes will also be listed here, but we can filter them out by testing nodeType . Changes in this code will allow / ignore other types of nodes as desired.

 function element_list(el,depth) { if (el.nodeType === 3) return; 
+12
source

Please note that the other answers are not true ...

It will also filter out the TEXT nodes, rather than displaying the BODY tag.

 function getDef(element, def) { var str = "" var childs = element.childNodes for (var i = 0; i < childs.length; ++i) { if (childs[i].nodeType != 3) { str += childs[i].nodeName + " " + def + "<br />" str += getDef(childs[i], def + 1) } } return str } // Example document.body.innerHTML = getDef(document.body, 0) 
 <div> <img src="foo.jpg"> <p> <span>bar</span> </p> </div> 
+5
source

Yes, you can! You will need to repeat some logic to create this tree, but for your example, you can do something like:

 var tracker = {}; Array.from(document.querySelectorAll("*")).forEach(node => { if (!tracker[node.tagName]) tracker[node.tagName] = 1; else tracker[node.tagName]++; }); console.log(tracker); 

You can change this to work on a recrusive subset of childNodes. It just repeats the whole document.

Check this script and open the console to see the result of the tracker , which counts and lists the tag names. To add depth, just take parentNode.length to the end.

Here the script is updated, which, it seems to me, corresponds to the number of depths;

 var tracker = {}; var depth = 0; var prevNode; Array.from(document.querySelectorAll("*")).forEach(node => { if (!tracker[node.tagName]) tracker[node.tagName] = 1; else tracker[node.tagName]++; console.log("Node depth:", node.tagName, depth); if (node.parentNode != prevNode) depth++; prevNode = node; }); console.log(tracker); 
+2
source

getElementDepth returns the absolute depth of the node (starting from the html node), to get the difference in depth between the two nodes, you can simply subtract the absolute depth from the other.

 function getElementDepthRec(element,depth) { if(element.parentNode==null) return depth; else return getElementDepthRec(element.parentNode,depth+1); } function getElementDepth(element) { return getElementDepthRec(element,0); } function clickEvent() { alert(getElementDepth(document.getElementById("d1"))); } 
 <!DOCTYPE html> <html> <body> <div> <div id="d1"> </div> </div> <button onclick="clickEvent()">calculate depth</button> </body> </html> 
+2
source

My initial solution went through each element to the DOM with a while to determine its depth:

 var el = document.querySelectorAll('body *'), //all element nodes, in document order depth, output= document.getElementById('output'), obj; for (var i = 0; i < el.length; i++) { depth = 0; obj = el[i]; while (obj.parentNode !== document.body) { //walk the DOM depth++; obj = obj.parentNode; } output.textContent+= depth + ' ' + el[i].tagName + '\n'; } 
 <div> <img src="foo.jpg"> <p> <span>bar</span> </p> </div> <hr> <pre id="output"></pre> 

I came up with a new solution that stores the depth of each element in the object. Because querySelectorAll() returns the elements in document order, parent nodes always appear in front of child nodes. Thus, the child depth of a node can be calculated as the depth of its parent node plus one.

Thus, we can determine the depths in one pass without recursion:

 var el = document.querySelectorAll('body *'), //all element nodes, in document order depths= { //stores the depths of each element [document.body]: -1 //initialize the object }, output= document.getElementById('output'); for (var i = 0; i < el.length; i++) { depths[el[i]] = depths[el[i].parentNode] + 1; output.textContent+= depths[el[i]] + ' ' + el[i].tagName + '\n'; } 
 <div> <img src="foo.jpg"> <p> <span>bar</span> </p> </div> <hr> <pre id="output"></pre> 
+1
source

Anyone looking for something that iterates through a tree under a node without using recursion *, but which also gives you depth (relative to the head of the node) ... as well as the coordinates of sibling-ancestor at all times:

 function walkDOM( headNode ){ const stack = [ headNode ]; const depthCountDowns = [ 1 ]; while (stack.length > 0) { const node = stack.pop(); console.log( '\ndepth ' + ( depthCountDowns.length - 1 ) + ', node: '); console.log( node ); let lastIndex = depthCountDowns.length - 1; depthCountDowns[ lastIndex ] = depthCountDowns[ lastIndex ] - 1; if( node.childNodes.length ){ depthCountDowns.push( node.childNodes.length ); stack.push( ... Array.from( node.childNodes ).reverse() ); } while( depthCountDowns[ depthCountDowns.length - 1 ] === 0 ){ depthCountDowns.splice( -1 ); } } } walkDOM( el ); 

PS it will be clear that I set > 0 and === 0 to try to improve clarity ... you can lower it first, and the second one can be replaced by a leading one ! , sure.

* look here for the terrible truth about the cost of recursion in JS (modern implementations for 2018-02-01 anyway!)

+1
source

You can do it using jQuery

 $('#divId').children().each(function () { // "this" is the current element }); 

and html should look like this:

 <div id="divId"> <img src="foo.jpg"> <p> <span>bar</span> </p> 

0
source
 (() => { const el = document.querySelectorAll('body *'); const depths = new Map(); depths.set(document.body, -1) el.forEach((e) => { const p = e.parentNode; const d = depths.get(p); depths.set(e, d + 1); }) return depths; })() 

This is Rick Hitchcock answer but using a map instead of an object

Results in Map(5) {body => -1, div => 0, img => 1, p => 1, span => 2}

0
source

Source: https://habr.com/ru/post/1236745/


All Articles