# 遍歷節(jié)點
Node 對象提供了一系列的屬性和方法用來利用 DOM 節(jié)點樹結構中節(jié)點的關系實現(xiàn)遍歷其中的節(jié)點。
> **關于節(jié)點之間的關系**,可以參考《DOM樹結構》一節(jié)有關節(jié)點之間關系的內容。
## 獲取父節(jié)點
通過 HTML 頁面中指定元素查找其父級節(jié)點,我們可以使用 Node 對象的 parentNode 屬性實現(xiàn):
```javascript
pNode = node.parentNode;
```
在上述語法結構中,parentNode 屬性返回指定節(jié)點的父節(jié)點。
> **值得注意的是:** 一個元素節(jié)點的父節(jié)點,可能是一個元素節(jié)點,也可能是一個文檔節(jié)點。
以下代碼示例,就是通過 parentNode 屬性獲取指定節(jié)點的父節(jié)點,再實現(xiàn)其他操作的:
```javascript
var btn = document.getElementById('btn');
var parentNode = btn.parentNode;
var className = parentNode.className;
className += ' animate';
parentNode.className = className;
```
### 獲取父元素節(jié)點
Node 對象除了提供了 parentNode 屬性可以獲取指定節(jié)點的父節(jié)點之外,還提供了 parentElement 屬性獲取指定節(jié)點的父元素節(jié)點。
```javascript
parentElementNode = node.parentElement;
```
在上述語法結構中,parentElement 屬性返回指定節(jié)點的父元素節(jié)點。
> **值得注意的是:** 如果一個節(jié)點沒有父節(jié)點,或者父節(jié)點不是一個元素節(jié)點的話,parentElement 屬性返回 null。
以下代碼示例,就是通過 parentElement 屬性獲取指定節(jié)點的父元素節(jié)點,再實現(xiàn)其他操作的:
```javascript
var btn = document.getElementById('btn');
var parentElement = btn.parentElement;
var className = parentElement.className;
className += ' animate';
parentElement.className = className;
```
### 父節(jié)點與父元素節(jié)點的區(qū)別
所謂父節(jié)點,并沒有指定某個節(jié)點的父節(jié)點一定是哪個類型的節(jié)點。而父元素節(jié)點,指定了某個節(jié)點的父節(jié)點一定是元素節(jié)點。
- parentNode: 獲取指定節(jié)點的父節(jié)點,其父節(jié)點不一定是元素節(jié)點。
- parentElement: 獲取指定節(jié)點的父元素節(jié)點,其父節(jié)點必須是元素節(jié)點。
如果我們獲取 `<html>` 元素的父節(jié)點的話,就是 document 文檔節(jié)點。而 document 文檔節(jié)點并不是一個元素節(jié)點。如下述代碼示例:
```javascript
// 獲取 <html> 元素
var html = document.documentElement;
console.log('parentNode: ' + html.parentNode);
console.log('parentElement: ' + html.parentElement);
```
上述代碼示例,輸出的結果如下:

## 獲取子節(jié)點
通過 HTML 頁面中指定元素查找其子節(jié)點,我們可以通過以下 Node 對象的屬性實現(xiàn):
| 屬性名 | 描述 |
| --- | --- |
| childNodes | 獲取指定節(jié)點的所有子節(jié)點 |
| firstChild | 獲取指定節(jié)點的第一個子節(jié)點 |
| lastChild | 獲取指定節(jié)點的最后一個子節(jié)點 |
> **值得注意的是:** HTML 頁面中某個元素的子節(jié)點不一定是唯一的。
### 獲取所有子節(jié)點
Node 對象提供了 childNodes 屬性用于獲取 HTML 頁面中指定節(jié)點的所有子節(jié)點:
```javascript
var ndList = Node.childNodes;
```
在上述語法結構中,childNodes 屬性的返回值 ndList 是一個 NodeList 對象,并且為只讀。該屬性獲取一個包含指定節(jié)點的所有子節(jié)點的集合。
> **值得注意的是:** childNodes 屬性返回的是一個動態(tài)的 NodeList 對象。有關動態(tài) NodeList 的內容,請參考《定位頁面元素》一節(jié)的內容。
以下代碼示例,就是通過指定節(jié)點獲取其所有子節(jié)點,再實現(xiàn)其他操作的:
```javascript
var parentNode = document.getElementById('parent');
var children = parentNode.childNodes;
console.log(children);
```
上述代碼示例,輸出的結果如下:

我們會發(fā)現(xiàn),獲取到的所有子節(jié)點,除了真正的子節(jié)點 `<button>` 之外,還具有 4 個文本節(jié)點。
### 空白節(jié)點
主流瀏覽器解析 HTML 頁面內容為 DOM 節(jié)點樹結構時,會產生空文本的空白節(jié)點。這是由 HTML 頁面源代碼中的換行引起的:
```html
<div id="parent" class="button-group">
<button id="btn" name="btn" class="button">A Button</button>
<button name="btn" class="button">A Button</button>
<button name="btn" class="button">A Button</button>
</div>
```
上述代碼示例的 DOM 節(jié)點樹結構如下圖所示:

如果將 HTML 頁面的源代碼編寫成一行時,這個空白節(jié)點的問題可以得到解決。但這種解決方式妨礙了我們代碼的可讀性,并不建議這樣解決。
> **值得注意的是:** IE 8 及之前版本的瀏覽器中不存在空白節(jié)點問題。
### 空白節(jié)點的解決方案
在開發(fā)中,空白節(jié)點的問題將 DOM 節(jié)點樹結構的解析及操作增加了不少的難度和麻煩。我們這里提供一種比較簡單有效的解決方式:
1. 棄用 DOM 中 Node 對象用于獲取指定節(jié)點的子節(jié)點和兄弟節(jié)點的屬性。
2. 通過使用 getElementsByTagName() 方法實現(xiàn)相應功能。
比如我們要查找 HTML 頁面指定元素的所有子節(jié)點的話,我們按照如下代碼示例實現(xiàn):
```javascript
var parentNode = document.getElementById('parent');
var children = parentNode.getElementsByTagName('button');
console.log(children);
```
上述代碼示例運行的結果如下:

> 關于 DOM 中為什么要具有空白節(jié)點以及更完整的解決方案,可以參考 Mozilla 社區(qū)的[《DOM 中的空白符》](https://developer.mozilla.org/zh-CN/docs/Web/Guide/API/DOM/Whitespace_in_the_DOM)。
### 獲取第一個子節(jié)點
Node 對象提供了 firstChild 屬性用于獲取指定節(jié)點的第一個子節(jié)點:
```javascript
var first_child = node.firstChild;
```
在上述語法結構中,firstChild 屬性返回的 first_child 表示當前節(jié)點的第一個子節(jié)點的引用。
> **值得注意的是:** 如果當前節(jié)點無子節(jié)點,則 firstChild 屬性返回?null。
以下代碼示例,就是通過指定節(jié)點獲取其第一個子節(jié)點,再實現(xiàn)其他操作的:
```javascript
var parentNode = document.getElementById('parent');
var firstChild = parentNode.firstChild;
```
通過上述代碼示例,我們最終得到依舊是空白節(jié)點,而并非第一個子節(jié)點。
```javascript
var parentNode = document.getElementById('parent');
var firstChild = parentNode.getElementsByTagName('button')[0];
console.log(firstChild);
```
通過 getElementsByTagName() 方法替換之后,所得到的節(jié)點才是真正的第一個節(jié)點。
### 獲取最后一個節(jié)點
Node 對象提供了 lastChild 屬性用于獲取指定節(jié)點的最后一個子節(jié)點:
```javascript
var last_child = node.lastChild;
```
在上述語法結構中,lastChild 屬性返回的 last_child 表示當前節(jié)點的最后一個子節(jié)點的引用。
> **值得注意的是:** 如果當前節(jié)點無子節(jié)點,則 lastChild 屬性返回?null。
以下代碼示例,就是通過指定節(jié)點獲取其最后一個子節(jié)點,再實現(xiàn)其他操作的:
```javascript
var parentNode = document.getElementById('parent');
var lastChild = parentNode.lastChild;
```
通過上述代碼示例,我們最終得到依舊是空白節(jié)點,而并非最后一個子節(jié)點。
```javascript
var parentNode = document.getElementById('parent');
var children = parentNode.getElementsByTagName('button');
var lastChild = children[children.length-1];
```
通過 getElementsByTagName() 方法替換之后,所得到的節(jié)點才是真正的最后一個節(jié)點。
## 獲取相鄰兄弟節(jié)點
通過 HTML 頁面中指定元素查找其相鄰兄弟節(jié)點,我們可以通過以下 Node 對象的屬性實現(xiàn):
| 屬性名 | 描述 |
| --- | --- |
| previousSibling | 獲取指定節(jié)點的前面相鄰兄弟節(jié)點 |
| nextSibling | 獲取指定節(jié)點的后面相鄰兄弟節(jié)點 |
### 獲取相鄰前面兄弟節(jié)點
Node 對象提供了 previousSibling 屬性用于獲取指定節(jié)點的前面相鄰兄弟節(jié)點:
```javascript
previousNode = node.previousSibling;
```
在上述語法結構中,previousSibling 屬性返回的 previousNode 表示當前節(jié)點的前一個兄弟節(jié)點。
> **值得注意的是:** 如果當前節(jié)點無前一個兄弟節(jié)點,則 previousSibling 屬性返回?null。
以下代碼示例,就是通過指定節(jié)點獲取其前面相鄰兄弟節(jié)點,再實現(xiàn)其他操作的:
```javascript
var elem = document.getElementById('btn');
var previousSibling = elem.previousSibling;
```
通過上述代碼示例,我們最終得到依舊是空白節(jié)點,而并非前面相鄰兄弟節(jié)點。
```javascript
// 判斷獲取的兄弟節(jié)點是否為元素節(jié)點
if ( previousSibling.nodeType != 1){
previousSibling = previousSibling.previousSibling;
}
```
通過判斷獲取的節(jié)點類型是否為元素節(jié)點,實現(xiàn)如何獲取真正的前面相鄰兄弟節(jié)點。
### 獲取相鄰后面兄弟節(jié)點
Node 對象提供了 nextSibling 屬性用于獲取指定節(jié)點的后面相鄰兄弟節(jié)點:
```javascript
nextNode = node.nextSibling;
```
在上述語法結構中,nextSibling 屬性返回的 nextNode 表示當前節(jié)點的后一個兄弟節(jié)點。
> **值得注意的是:** 如果當前節(jié)點無后一個兄弟節(jié)點,則 nextSibling 屬性返回?null。
以下代碼示例,就是通過指定節(jié)點獲取其后面相鄰兄弟節(jié)點,再實現(xiàn)其他操作的:
```javascript
var elem = document.getElementById('btn');
var nextSibling = elem.nextSibling;
```
通過上述代碼示例,我們最終得到依舊是空白節(jié)點,而并非后面相鄰兄弟節(jié)點。
```javascript
// 判斷獲取的兄弟節(jié)點是否為元素節(jié)點
if ( nextSibling.nodeType != 1){
nextSibling = nextSibling.nextSibling;
}
```
通過判斷獲取的節(jié)點類型是否為元素節(jié)點,實現(xiàn)如何獲取真正的后面相鄰兄弟節(jié)點。
- 關于
- 第一章 DOM 是什么
- 第一節(jié) DOM 介紹
- 第二節(jié) DOM 樹結構
- 第二章 Document 對象
- 第一節(jié) Document 對象介紹
- 第二節(jié) 定位頁面元素
- 第三節(jié) 創(chuàng)建頁面元素
- 第三章 Node 對象
- 第一節(jié) Node 對象介紹
- 第二節(jié) 判斷節(jié)點類型
- 第三節(jié) 遍歷節(jié)點
- 第四節(jié) 插入節(jié)點
- 第五節(jié) 刪除節(jié)點
- 第六節(jié) 替換節(jié)點
- 第七節(jié) 復制節(jié)點
- 第八節(jié) textContent 屬性
- 第四章 Element 對象
- 第一節(jié) Element 對象介紹
- 第二節(jié) DOM 元素樹
- 第三節(jié) 定位頁面元素
- 第四節(jié) 遍歷元素
- 第五節(jié) 屬性操作
- 第六節(jié) innerHTML 屬性
- 第五章 樣式操作
- 第一節(jié) 獲取內聯(lián)樣式
- 第二節(jié) 獲取外聯(lián)樣式表
- 第三節(jié) 獲取 class 屬性
- 第四節(jié) 獲取當前有效樣式
- 第五節(jié) 設置內聯(lián)樣式
- 第六節(jié) 設置 class 屬性
- 第七節(jié) Element 對象的樣式屬性
- 第六章 事件
- 第一節(jié) 什么是事件
- 第二節(jié) 注冊事件
- 第三節(jié) 移除注冊事件
- 第四節(jié) Event 事件對象
- 第五節(jié) 獲取目標元素
- 第六節(jié) 阻止默認行為
- 第七節(jié) 獲取鼠標坐標
- 第八節(jié) 事件流
- 第九節(jié) 事件委托
- 第七章 表單操作
- 第一節(jié) 獲取表單
- 第二節(jié) 表單操作
- 第三節(jié) 表單驗證
- 第四節(jié) 表單提交
- 第八章 BOM
- 第一節(jié) BOM 是什么
- 第二節(jié) Window 對象
- 第三節(jié) Navigator 對象
- 第四節(jié) History 對象
- 第五節(jié) Location 對象
- 第六節(jié) 定時器
