我们一般可以采用DFS(深度优先遍历)
和BFS(广度优先遍历)
来遍历DOM树
介绍 DFS & BFS
我们来结合具体例子进行分析,给出HTML代码片段如下:
DFS
总是先进入下一级节点,只有当下一级没有未遍历的子节点时才会进入到当前层级的其它节点。对于上面例子DFS
遍历结果应为:
root, container, sidebar, menu, main, post, copyright
BFS
则总是先遍历当前层级的所有节点,只有当当前层级所有节点都遍历结束后才会进入下一层级。对于上面例子BFS
遍历结果应为:
root, container, sidebar, main, menu, post, copyright
DFS的具体实现
DFS
主要采用递归实现,依次遍历节点,如果遍历到的节点有子节点,则开始遍历子节点
const DFSTraverse = (rootNodes, rootLayer) => { const roots = Array.from(rootNodes) while (roots.length) { const root = roots.shift() printInfo(root, rootLayer) // 如果有子节点,直接遍历子节点,同时将层级加1 if (root.children.length) { DFSTraverse(root.children, rootLayer + 1) } } }
BFS的具体实现
BFS
采用队列的思想,采用出队的方式遍历节点,如果遍历到的节点有子节点,则将子节点入队(这里处理节点层级的方式比DFS
更复杂一些,因为这里将所有节点都放到了同一个数组中进行处理)
const BFSTraverse = (rootNodes, rootLayer) => { const roots = Array.from(rootNodes) const rootsLayer = [] // 单用一个数组存放每个节点的的层级 // 初始化 for (let i = 0; i < roots.length; i++) { rootsLayer.push(rootLayer) } var rootIdx = 0 // 记录当前处理roots中的第几个节点,方便查找rootsLayer中对应的层级 while (roots.length) { const root = roots.shift() // 出队 printInfo(root, rootsLayer[rootIdx]) // 如果有子节点,将子节点放到roots队列中 if (root.children.length) { Array.prototype.push.apply(roots, Array.from(root.children)) // 将当前层级加1得到子节点的层级 rootLayer = rootsLayer[rootIdx] + 1 for (let i = 0; i < root.children.length; i++) { rootsLayer.push(rootLayer) } } // 处理下一个root节点 rootIdx++ } }
结果
先给大家补全代码:
// 输入节点信息const printInfo = (node, layer) => { var str = '' for (let i = 1; i < layer; i++) { str += ' ' } console.log(`${layer}:${str}${node.tagName} .${node.className}`); }console.log('DFS *******************************');DFSTraverse(document.querySelectorAll('.root'), 1);console.log('BFS *******************************');BFSTraverse(document.querySelectorAll('.root'), 1);
上面例子的运行结果为: