原文链接:[如何遍历整个DOM树(外网原文链接)](https://chrisdeo.github.io/2019/07/20/%E5%A6%82%E4%BD%95%E9%81%8D%E5%8E%86%E6%95%B4%E4%B8%AADOM%E6%A0%91/)
作为前端开发工程师,我们大部分工作内容其实还是围绕着DOM在进行Javascript的编写;为了获取对应的DOM节点,我们通常会使用选择器来直接获取对应的元素。但如果让我们访问一整棵DOM树,针对某个环节进行操作呢?这就需要我们对DOM的基本属性以及树的数据结构有比较深刻的认识了。
nodeType:
在开始遍历操作前,我们先要知道DOM元素nodeType这个属性的意义,它以数字值返回指定节点的节点类型,我们这里只例举常见的几种:
① nodeType
为1
时,表明该节点为元素节点,如body、div等;
② nodeType
为2
时,表明该节点为属性节点,啥是属性节点呢,其实就是src、target这种,只不过我们平常都是以属性来访问它们而不是将其当属性节点提取出;
③ nodeType
为3
时,表明该节点为文本节点。
DFS:
在知道以上的基本要素后,我们就可以用深度遍历(DFS)的方式开始递归遍历DOM树:
function traverseByDFS(root){
if(!root) return;
if(root.nodeType === 1){
let len = root.children.length;
console.log(root.nodeName + " ." + root.className);
for(let i=0;i<len;i++){
traverseByDFS(root.children[i]);
}
}
}
例如以下这段代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="root">
<div class="container">
<section class="sidebar">
<ul class="menu"></ul>
</section>
<section class="main">
<article class="post"></article>
<p class="copyright"></p>
</section>
</div>
</div>
</body>
<script>
function traverseByDFS(root){
if(!root) return;
if(root.nodeType === 1){
let len = root.children.length;
console.log(root.nodeName + " ." + root.className);
for(let i=0;i<len;i++){
traverseByDFS(root.children[i]);
}
}
}
traverseByDFS(document.getElementsByClassName('root')[0]);
</script>
</html>
以上代码的运行结果为:
BFS:
对于DOM树的广度遍历来说,关键是如何保存同层节点的访问顺序以便之后继续对他们的子节点进行遍历,而DOM树本身就是构造完整的,我们直接访问对应节点属性就可以拿到相邻元素、祖先元素以及后代元素的值。所以只需要结合队列的特性就可以保存顺序再通过递归访问即可遍历所有元素。
BFS代码如下:
function traverseByBFS(root){
if(!root) return;
let queue = [];
let rootFirstKid = root.firstElementChild;
if (rootFirstKid) {
queue.unshift(rootFirstKid);
console.log(queue[0].nodeName + ' .' + queue[0].className);
while (rootFirstKid.nextElementSibling) {
queue.unshift(rootFirstKid.nextElementSibling);
console.log(queue[0].nodeName + ' .' + queue[0].className);
rootFirstKid = rootFirstKid.nextElementSibling;
}
while (queue.length) {
let whoIsOut = queue.pop(); // 取队列的第一个,其实是在数组的尾部
traverseByBFS(whoIsOut);
}
}
}
例如以下这段代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="root">
<div class="container">
<section class="sidebar">
<ul class="menu"></ul>
</section>
<section class="main">
<article class="post"></article>
<p class="copyright"></p>
</section>
</div>
</div>
</body>
<script>
function traverseByBFS(root){
if(!root) return;
let queue = [];
let rootFirstKid = root.firstElementChild;
if (rootFirstKid) {
queue.unshift(rootFirstKid);
console.log(queue[0].nodeName + ' .' + queue[0].className);
while (rootFirstKid.nextElementSibling) {
queue.unshift(rootFirstKid.nextElementSibling);
console.log(queue[0].nodeName + ' .' + queue[0].className);
rootFirstKid = rootFirstKid.nextElementSibling;
}
while (queue.length) {
let whoIsOut = queue.pop(); // 取队列的第一个,其实是在数组的尾部
traverseByBFS(whoIsOut);
}
}
}
traverseByBFS(document.getElementsByClassName('root')[0]);
</script>
</html>
以上代码的运行结果为: