节点层级
DOM中将HTML文档视为树结构,一个HTML文件可以看作是所有元素组成的一个节点树,各元素节点之间有级别的划分。具体示例如下。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<a href="#">链接</a>
<p>段落...</p>
</body>
</html>
在上述代码中,DOM根据HTML中各节点的不同作用,可将其分别划分为标签节点、文本节点和属性节点。其中,标签节点也被称为元素节点,HTML文档中的注释则单独叫做注释节点。
节点之间的层级关系,最常见的是父子兄层级关系,下面我们以<head>、<body>与<html>节点为例进行介绍,具体如下。
根节点:<html>标签是整个文档的根节点,有且仅有一个。
父节点:指的是某一个节点的上级节点,例如,<html>元素则是<head>和<body>的父节点。
子节点:指的是某一个节点的下级节点,例如,<head>和<body>节点是<html>节点的子节点。
兄弟节点:两个节点同属于一个父节点,例如,<head>和<body>互为兄弟节点。
在讲解了各种节点的层次关系后,接下来我们针对各层级节点的获取进行详细讲解。
1. 获取父级节点
在JavaScript中,可以使用parentNode属性来获得离当前元素的最近的一个父节点,如果找不到父节点就返回为 null,语法格式为:obj.parentNode。其中,obj是一个DOM对象,是通过获取元素的方法来获取的元素,如getElementById()方法等。
接下来我们通过案例形式演示如何获取当前元素的父节点,示例代码如下。
1 <body>
2 <div class="demo">
3 <div class="box">
4 <span class="child">span元素</span>
5 </div>
6 </div>
7 <script>
8 var child = document.querySelector('.child');
9 console.log(child.parentNode);
10 </script>
11 </body>
上述代码中,第8行通过querySelector()获取类名为child的span元素,存储到child对象中。第9行会在控制台输出离child元素最近的父级节点(box) 。浏览器预览效果如图1所示。
图1 父级节点
2. 获取子级节点
在JavaScript中,可以使用childNodes属性或者children属性两种方式来获得当前元素的所有子节点的集合,接下来我们就分别介绍这两种方式的用法。
(1) childNodes属性
childNodes属性获得的是当前元素的所有子节点的集合,该集合为即时更新的集合。下面我们通过案例的形式演示如何获取当前元素子节点,示例代码如下。
1 <body>
2 <ul>
3 <li>我是li</li>
4 <li>我是li</li>
5 <li>我是li</li>
6 <li>我是li</li>
7 </ul>
8 <script>
9 // DOM 提供的方法(API)获取
10 var ul = document.querySelector('ul');
11 var lis = ul.querySelectorAll('li');
12 console.log(lis);
13 console.log(ul.childNodes);
14 console.log(ul.childNodes[0].nodeType);
15 console.log(ul.childNodes[1].nodeType);
16 </script>
17 </body>
上述代码中,第2~7行定义了一个无序列表ul。第10~12行代码用于测试DOM提供的API方法是否能够获取全部li元素。第13行代码使用节点的方式获取ul下的所有的子节点(包括文本节点和元素节点)。第14、15行代码根据nodeType属性来获取文本节点和元素节点的节点类型。输出结果如图2所示。
图2 子级节点
在图2中,childNodes属性返回的是NodeList对象的集合,返回值里面包含了元素节点、文本节点等其他类型的节点。如果想要获取childNodes里面的元素节点,需要做专门的处理。在第15行代码下面编写如下代码获取元素节点。
1 for (var i = 0; i < ul.childNodes.length; i++) {
2 if (ul.childNodes[i].nodeType === 1) {
3 console.log(ul.childNodes[i]);
4 }
5 }
上述代码中,ul.childNodes[i]是元素节点。需要注意的是,childNodes属性在IE 6~IE 8中不会获取文本节点,在IE 9及以上版本和主流浏览器中则可以获取文本节点。所以在实际开发中一般不提倡使用childNodes。
(2) children
children是一个可读的属性,返回所有子元素节点。children只返回子元素节点,其余节点不返回。目前各大浏览器都支持该属性,在实际开发中推荐使用children。下面我们以案例的形式演示如何获取当前元素子节点,示例代码如下。
1 <body>
2 <ol>
3 <li>我是li</li>
4 <li>我是li</li>
5 <li>我是li</li>
6 <li>我是li</li>
7 </ol>
8 <script>
9 var ul = document.querySelector('ol');
10 var lis = ul.querySelectorAll('li');
11 console.log(ul.children);
12 </script>
13 </body>
上述代码中,第11行通过ul.children属性获取ul元素的子元素,在控制台中的输出结果为HTMLCollection(4) [li, li, li, li]。
(3) 获取子节点
firstChild属性和lastChild属性,前者返回第一个子节点,后者返回的是最后一个子节点,如果找不到则返回null。需要注意的是它们的返回值包括文本节点和元素节点等。
(4) 获取子元素节点
firstElementChild属性和lastElementChild属性,前者返回第一个子元素节点,后者返回最后一个子元素节点,如果找不到则返回null。需要注意的是,这两个属性有兼容性问题,IE 9以上才支持。
实际开发中,firstChild 和lastChild 包含其他节点,操作不方便;而firstElementChild和lastElementChild又有兼容性问题,那么如何获取第一个子元素节点或最后一个子元素节点呢?为了解决兼容性问题,在实际开发中通常使用“obj.children[索引] ”的方式来获取子元素节点。示例代码如下。
obj.children[0] // 获取第一个子元素节点
obj.children[obj.children.length - 1] // 获取最后一个子元素节点
3. 获取兄弟节点
在JavaScript中,可以使用nextSibling属性来获得下一个兄弟节点,使用previousSibling属性来获得上一个兄弟节点。它们的返回值包含元素节点或者文本节点等。如果找不到,就返回null。
如果想要获得兄弟元素节点,则可以使用nextElementSibling返回当前元素的下一个兄弟元素节点,使用previousElementSibling属性返回当前元素的上一个兄弟元素节点。如果找不到则返回null。要注意的是,这两个属性有兼容性问题,IE 9以上才支持。
实际开发中,nextSibling和previousSibling属性返回值都包含其他节点,操作不方便,而nextElementSibling和previousElementSibling又有兼容性问题。为了解决兼容性问题,在实际开发中通常使用封装函数来处理兼容性。示例代码如下。
1 function getNextElementSibling(element) {
2 var el = element;
3 while (el = el.nextSibling) {
4 if (el.nodeType === 1) {
5 return el;
6 }
7 }
8 return null;
9 }
上述代码中,第1行代码定义了getNextElementSibling()函数,参数为element。在while循环中,通过if条件语句进行判断,如果nodeType属性值等于1,那么获取到的节点类型为元素节点。