我們先來看一張DOM Tree的圖
DOM 節點有分層的概念,節點與節點之間的關係我們大致上可以分成兩種:
父子關係:
除了
document
之外,每一個節點都會有個上層的節點,我們通常稱之為「父節點」 (Parent node),而相對地,從屬於自己下層的節點,就會稱為「子節點」(Child node)。兄弟關係:有同一個「父節點」的節點,那麼他們彼此之間就是「兄弟節點」(Siblings node)。
有時候你無法直接用名稱或選擇器找到節點,這時候就必須透過關係。
簡單解釋一下這個流程,依然拿之前的案例來做說明:
這是一篇文章其中有一篇文章的標題是Professional Sports
,當今天我必須要回傳在這個標題裡所有放在<li>
的文字內容(別問我為什麼要回傳XD),這時候在DOM Tree的查找路徑會是:
從<h4>Professional Sports</h4>
出發。
找到他的兄弟<ul>
。
在找到<ul>
的children(孩子)<li>
。
而這個從樹狀結構裡查找資料的動作就是我們今天的主題**traverse**
。
選出周邊的元素節點
我們來看一下語法:
- **
parentElement
**:返回元素的父級元素。 - **
nextElementSibling
**:返回元素的下一個兄弟元素。 - **
previousElementSibling
**:返回元素的前一個兄弟元素。 - **
firstElementChild
**:返回元素的第一個子元素。 - **
lastElementChild
**:返回元素的最後一個子元素。
我們在之前那個案例來實際操作一下吧:
其中要特別說一下最一開始的HTMLCollection(5)
,還記得昨天我們有說到NodeList
嗎?
如果忘記也沒關係,我們在看一下:
NodeList
很類似於Array ,但它並不是真正的 Array。
它不能新增刪除元素,只有幾個簡單的唯讀操作:
- 查看長度
length
- 遍歷內容
forEach
- 使用 index 來存取特定項目
像 HTMLCollection()
跟 NodeList
很類似於Array ,但它並不是真正的 Array的,我們有一個專有的名詞,叫做Array-like object
但一般我們都會叫他Array-like
。
詳細的內容這邊就不說了,有興趣的朋友再去網路上看。
另外一組語法
大家如果在網路上看資料時,想必都會看到另外一組語法:
parentNode
、nextSibling
、previousSibling
、firstChild
和 lastChild
為什麼會有兩組呢?我們先來看一下實際操作後大家應該就會知道差別在哪裡了:
為什麼這組語法出來的結果會有這麼多的text啊?主要還是我們在排版程式碼時的「換行/縮排」,會產生隱藏的文字節點。詳細內容大家可以到 MDN 看,我這邊就是簡單提一下。
至於這兩種語法的區別,我個人的解讀是在於它們如何處理節點。其中 parentElement
和 nextElementSibling
等屬性和方法只會考慮元素節點(Element Node),而忽略其他類型的節點,如文字節點、註釋節點等。而 parentNode
和 nextSibling
等屬性和方法則會考慮所有類型的節點。
這是我自己的解讀,如果有錯還請大家指教!
我在知乎跟稀土上有看到兩篇文章,大家也可以參考一下:知乎參考文章、稀土參考文章
陣列遍歷
當我們想對一個陣列裡的每個元素都執行某個方法時,除了傳統的 for 迴圈外,還可以使用什麼呢?就讓我們一起看下去:
for
使用情境:
- 用於執行迴圈次數「明確」的狀況
- 例如:印出陣列
[0, 1, 2]
中的每一個數字
1 | for(let i = 0; i < 3; i++){ |
arr.forEach
使用情境:
- 用法與 for loop 類似,差別在於 forEach 不需設定索引的邊界條件
- 缺點是無法使用 break 中斷迴圈,或 return 返回值等等
語法:
1 | array.forEach(function(currentValue, index, arr), thisValue) |
currentValue :當前陣列元素的值
index :當前陣列元素的 index
arr :當前陣列本身。
我們用一個範例來解釋:
1 | const scores = [1, 2, 3] |
由於這個函式只會在 forEach
裡面運作,別的地方用不到,因此沒有特別取名的必要。因此可以使用箭頭函式:
1 | const scores = [1, 2, 3] |
其結果為:
有兩點要注意:
forEach
沒有回傳值:forEach
方法的角色是領路人,把陣列中的每個元素依序交給 function 處理,交付完它的任務就結束了。- function 的參數可以自由命名:
慣用的命名手法是陣列用複數單字命名 (例如 words, scores ),代表單一陣列元素的參數就用單數命名 (例如 word, score)。
另外 item 和 element 也常被用來代表單一元素。
for…of
語法:
1 | for (variable of iterable) |
variable
:在每次迭代中從序列中接收一個值。可以是使用 const、let 或 var 進行聲明,也可以是分配目標(例如之前聲明的變數、物件屬性或解構賦值模式)。- **
iterable
**:一個可迭代的 object。 作為迴圈運作的值序列的來源。 statement
:在每次迭代中要執行的陳述式。
一樣我們用一個範例來解釋:
1 | const array1 = ['a', 'b', 'c']; |
至於跟他很像的for…in我很少用到比較沒有那麼清楚,這邊附上PJ大神所寫的文章讓大家參考。
map
語法:
1 | const newArray = array.map((currentValue, index, array) => { |
currentValue
: 目前迭代的陣列元素的值。index
(選填): 目前迭代的陣列元素的索引。array
(選填): 原始的陣列。
map()
方法的主要特點是它不會改變原始陣列,而是返回一個新的陣列,其中每個元素都是經過回呼函式處理後的結果。
看個案例會更加清楚:
1 | const array1 = [1, 4, 9, 16]; |
終於寫完了,但有幾個遍歷的方法我並沒有寫出來,是因為這邊所列的都是我在這段學習過程中有使用到且還記得的><”
沒寫出來的應該是我沒用過、或是我真的忘了。
但很多資料也都只寫到這些,應該還行啦。