DOM遍历,可以简单理解为“查找元素”。举个例子,如果你使用getElementById()等方法获取一个元素,然后又想得到该元素的父元素、子元素,甚至是下一个兄弟元素,这就是DOM遍历。

你至少要知道DOM遍历是查找元素的意思,因为很多地方都用到这个术语。在JavaScript中,对于DOM遍历,可以分为以下3种情况。

  • (1)查找父元素。
  • (2)查找子元素。
  • (3)查找兄弟元素。

DOM遍历,也就是查找元素,主要以“当前所选元素”为基点,然后查找它的父元素、子元素或者兄弟元素。

一、查找父元素

在JavaScript中,我们可以使用parentNode属性来获得某个元素的父元素。

语法:

obj.parentNode

说明:

obj是一个DOM对象,指的是使用getElementById()、getElementsByTagName()等方法获取的元素。

举例:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <style type="text/css"> table{border-collapse:collapse;} table,tr,td{border:1px solid gray;} </style> <script> window.onload = function () { var oTd = document.getElementsByTagName("td"); //遍历每一个td元素 for (var i = 0; i < oTd.length; i++) { //为每一个td元素添加点击事件 oTd[i].onclick = function () { //获得当前td的父元素(即tr) var oParent = this.parentNode; //为当前td的父元素添加样式 oParent.style.color = "white"; oParent.style.backgroundColor = "red"; }; } } </script> </head> <body> <table> <caption>考试成绩表</caption> <tr> <td>小明</td> <td>80</td> <td>80</td> <td>80</td> </tr> <tr> <td>小红</td> <td>90</td> <td>90</td> <td>90</td> </tr> <tr> <td>小杰</td> <td>100</td> <td>100</td> <td>100</td> </tr> </table> </body> </html>

浏览器预览效果如下图所示。

分析:

这个例子实现的效果是:当我们随便点击一个单元格时,就会为该单元格所在的行设置样式。也就是说,我们要找到当前td元素的父元素(即tr)。如果我们尝试使用querySeletor()和querySelectorAll()是没办法实现的。

不少初学者在接触DOM操作的时候,不知道什么时候用类数组,什么时候不用类数组?其实凡是单数的就不用,例如parentNode只有一个,何必用数组呢?

二、查找子元素

在JavaScript中,我们可以使用以下两组方式来获得父元素中的所有子元素或某个子元素。

  • (1)childNodes、firstChild、lastChild
  • (2)children、firstElementChild、lastElementChild

其中,childNodes获取的是所有的子节点。注意,这个子节点是包括元素节点以及文本节点的。而children获取的是所有的元素节点,不包括文本节点。

举例:childNodes与children的比较

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script> window.onload = function () { var oUl = document.getElementById("list"); var childNodesLen = oUl.childNodes.length; var childrenLen = oUl.children.length; alert("childNodes的长度为:" + childNodesLen + "\n" + "children的长度为:" + childrenLen); } </script> </head> <body> <ul id="list"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> </ul> </body> </html>

浏览器预览效果如下图所示。

分析:

children.length获取的是元素节点的长度,返回结果为3,这个我们没有疑问。childNodes.length获取的是子节点的长度,返回结果却是7,这是怎么回事呢?

其实对于ul元素来说,childNodes包括了3个子元素节点和4个子文本节点。我们可以看到每一个li元素之间都是换行的,对吧?每一次换行都是一个空白节点,JavaScript会把这些空白节点当成文本节点来处理,如下图所示。

再回到这个例子,由于每一次换行都是一个子节点,我们数一下就知道是4个。注意,第一个li前面也有一次换行,最后一个li后面也有一次换行,因为都在ul元素里面嘛,肯定要算上的。

举例:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script> window.onload = function () { var oBtn = document.getElementById("btn"); var oUl = document.getElementById("list"); oBtn.onclick = function () { oUl.removeChild(oUl.lastChild); } } </script> </head> <body> <ul id="list"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> <li>jQuery</li> <li>Vue.js</li> </ul> <input id="btn" type="button" value="删除" /> </body> </html>

浏览器预览效果如下图所示。

分析:

当我们尝试点击【删除】按钮时,会发现一个很奇怪的现象:需要点击两次才可以删除一个li元素!

为什么会这样呢?其实好多小伙伴都忘了:两个元素之间的“换行空格”其实也是一个节点。因此在删除节点的时候,第1次点击删除的是“文本节点”,第2次点击删除的才是li元素。解决办法有两个。

  • (1)将li元素间的“换行空格”去掉。
  • (2)使用nodeType来判断:我们都知道,元素节点的nodeType属性值为1,文本节点的nodeType属性值为3。然后使用if判断,如果oUl.lastChild.nodeType值为3,则执行removeChild()两次,第1次删除“空白节点”,第2次删除元素。如果oUl.lastChild.nodeType值不为3,则只执行removeChild()一次。

举例:改进后的代码

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script> window.onload = function () { var oBtn = document.getElementById("btn"); var oUl = document.getElementById("list"); oBtn.onclick = function () { if (oUl.lastChild.nodeType == 3) { oUl.removeChild(oUl.lastChild); oUl.removeChild(oUl.lastChild); } else { oUl.removeChild(oUl.lastChild); } } } </script> </head> <body> <ul id="list"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> <li>jQuery</li> <li>Vue.js</li> </ul> <input id="btn" type="button" value="删除" /> </body> </html>

浏览器预览效果如下图所示。

分析:

从上面我们也可以看出来了,使用childNodes、firstChild、lastChild这几个来操作元素节点是非常麻烦的,因为它们都把文本节点(一般是空白节点)算进来了。实际上,上面这种是旧的做法,JavaScript为了让我们可以快速开发,提供了新的方法,也就是只针对元素节点的操作属性:children、firstElementChild、lastElementChild。

举例:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script> window.onload = function () { var oBtn = document.getElementById("btn"); var oUl = document.getElementById("list"); oBtn.onclick = function () { oUl.removeChild(oUl.lastElementChild); } } </script> </head> <body> <ul id="list"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> <li>jQuery</li> <li>Vue.js</li> </ul> <input id="btn" type="button" value="删除" /> </body> </html>

浏览器预览效果如下图所示。

分析:

这里我们使用oUl.removeChild(oUl.lastElementChild);一句代码就可以轻松搞定。此外,firstElementChild获取的是第一个子元素节点,lastElementChild获取的是最后一个子元素节点。如果我们想要获取任意一个子元素节点,可以使用children[i]的方式来实现。

三、查找兄弟元素

在JavaScript中,我们可以使用以下2组方式来获得兄弟元素。

  • (1)previousSibling、nextSibling
  • (2)previousElementSibling、nextElementSibling

previousSibling查找前一个兄弟节点,nextSibling查找后一个兄弟节点。previousElementSibling查找前一个兄弟元素节点,nextElementSibling查找后一个兄弟元素节点。

跟查找子元素的两组方式一样,previousSibling和nextSibling查找出来的可能是文本节点(一般是空白节点),因此如果你希望只操作元素节点,建议使用previousElementSibling和nextElementSibling。

举例:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script> window.onload = function () { var oBtn = document.getElementById("btn"); var oUl = document.getElementById("list"); oBtn.onclick = function () { var preElement = oUl.children[2].previousElementSibling; oUl.removeChild(preElement); }; } </script> </head> <body> <ul id="list"> <li>HTML</li> <li>CSS</li> <li>JavaScript</li> <li>jQuery</li> <li>Vue.js</li> </ul> <input id="btn" type="button" value="删除" /> </body> </html>

浏览器预览效果如下图所示。

分析:

我们实现的是把第3个列表项前一个兄弟元素删除。这里如果用previousSibling 来代替previousElementSibling,就实现不了了。

常见问题:

1.DOM遍历提供的这些查找方法,跟之前“9.4 获取元素”一节介绍的获取元素方法有什么不同?

DOM遍历这些方法其实就是对“9.4 获取元素”一节中那些方法的一个补充。DOM遍历中的方法,让我们可以实现前者所无法实现的操作,例如获取某一个元素的父元素、获取当前点击位置下的子元素等。