通过HTML DOM迭代并获得深度

问题描述 投票:8回答:8

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

例:

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

会导致

  • div 0
  • img 1
  • 第1页
  • 跨度2
javascript html dom
8个回答
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;

5
投票

请注意,其他答案是不正确的...

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

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>

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的递归子集上运行。这只是迭代整个文档。

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

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>

1
投票

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

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>

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

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

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
投票

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

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的当代实现!)


0
投票

您可以使用Jquery来完成此操作

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

并且html应该如下所示:

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


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 Hitchcocks答案,但使用Map而不是object

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

© www.soinside.com 2019 - 2024. All rights reserved.