通过HTML DOM迭代并获得深度

时间:2015-11-24 21:13:28

标签: javascript html dom

如何迭代HTML DOM并在javascript中列出所有具有深度的节点。

示例:

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

会导致

  • div 0
  • img 1
  • p 1
  • span 2

8 个答案:

答案 0 :(得分:12)

编写跟踪深度的递归函数:

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);

正如CodeiSir所指出的,这也会列出文本节点,但我们可以按testing the nodeType过滤掉它们。此代码的变体将允许/忽略其他节点类型。

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

答案 1 :(得分:5)

请注意,其他答案是/无法正确纠正......

这也将过滤掉“TEXT”节点,而不输出BODY标签。

for ( LinkedList<Integer> bucket : bucketsOut ) {
    if (bucket.peekFirst() != null) {
        input[i] = bucket.pollFirst().intValue();
        break; 
    }
}
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)

答案 2 :(得分:2)

是的,你可以!您必须迭代并使用一些逻辑来创建此树,但是对于您的示例,您可以执行以下操作:

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

您可以将其修改为在childNodes的recrusive子集上运行。这只是迭代整个文档。

选中this fiddle并打开控制台,查看tracker的输出,该输出计算并列出标记名称。要添加深度,只需抓住parentNode.length即可。

这是一个更新的脚本,我认为深度计算是否合适;

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);

答案 3 :(得分:2)

getElementDepth返回节点的绝对深度(从html节点开始),以获得两个节点之间的深度差异,您可以从另一个节点中减去绝对深度。

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>

答案 4 :(得分:1)

我的原始解决方案使用fsfs.conf循环将每个元素向上移动到DOM以确定其深度:

&#13;
&#13;
while
&#13;
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';
}
&#13;
&#13;
&#13;

我提出了一个新的解决方案,它将每个元素的深度存储在一个对象中。由于querySelectorAll()以文档顺序返回元素,因此父节点始终显示在子节点之前。因此,子节点的深度可以计算为其父节点的深度加1。

这样,我们可以在没有递归的情况下确定单次传递的深度:

&#13;
&#13;
<div>
  <img src="foo.jpg">
  <p>
    <span>bar</span>
  </p>
</div>
<hr>
<pre id="output"></pre>
&#13;
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';
}
&#13;
&#13;
&#13;

答案 5 :(得分:1)

任何寻找在节点下遍历树的东西而不使用递归*但是也给你深度(相对于头节点)......以及兄弟 - 祖先坐标的任何人:

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我会理解我已经放入> 0=== 0来尝试提高清晰度...首先可以省略,第二个可以用前导!取代当然。

*看看here关于JS中递归成本的令人震惊的事实(无论如何,2018-02-01的当代实现!)

答案 6 :(得分:0)

您可以使用Jquery

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

,html应如下所示:

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

答案 7 :(得分:0)

(() => {
    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;
})()

这是Rick Hitchcock的答案,但使用Map而不是object

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