iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Modern Web

我的JavaScript日常系列 第 24

JavaScript Day 24. DOM API 節點

  • 分享至 

  • xImage
  •  

之前我們有提到,JavaScript 如何與瀏覽器溝通,於是我們討論到透過 JavaScript 取得 DOM API 節點,使瀏覽器可以處理使用者行為與互動。

DOM 是 W3C 制定的一個規範,是 HTML 與 XML 文檔的編程接口,一個 HTML 文件,我們可以用編輯器以代碼的形式展示,也可以用瀏覽器以頁面的形式展示,DOM 將文件檔解析為一個由節點和對象 ( 包含屬性和方法 ) 組成的結構集合,因此開發者能夠在一個規範及方法之下改變內容、結構及樣式。


如何訪問 DOM

我們可以透過 JavaScript 來獲取 DOM 的 document 和 window 節點 API,來操作裡面的文件以及訊息。而 DOM 的節點大致歸納為:元素節點、文字節點以及屬性節點。

以下為常見的 DOM 選取方法:

// 根據傳入的值,找到 DOM 中 id 為 'xxx' 的元素。
document.getElementById('xxx');

// 針對給定的 tag 名稱,回傳所有符合條件的 NodeList 物件
document.getElementsByTagName('xxx');

// 針對給定的 class 名稱,回傳所有符合條件的 NodeList 物件。
document.getElementsByClassName('xxx');

// 針對給定的 Selector 條件,回傳第一個 或 所有符合條件的 NodeList。
document.querySelector('xxx');
document.querySelectorAll('xxx');

DOM 節點類型

Node 是一個接口,有許多接口都從 Node 繼承方法與屬性,下面為常用的 DOM 節點:

節點 數值 說明
Node.ELEMENT_NODE 1 一個元素節點,例如 <p> 和 <div>
Node.TEXT_NODE 3 實際文字節點,包括了換行與空格
Node.COMMENT_NODE 8 一個 Comment 節點
Node.DOCUMENT_NODE 9 一個 Document 節點
Node.DOCUMENT_TYPE_NODE 10 描述文檔類型的 DocumentType 節點。例如 <!DOCTYPE html> 就是用於 HTML5 的
Node.DOCUMENT_FRAGMENT_NODE 11 一個 DocumentFragment 節點

假如我們要判斷一個 Node 是否為一個元素,可以透過以上表格得知屬性值,並使用 nodeType

if(X.nodeType === 1){
console.log('X 是一個元素');
}

節點關係 API

先前我們也有談論到「DOM Tree」,因此可以知道 DOM 是一層一層的,需要有效的抓取元素則需要弄清楚這一層一層的關係。

  • 父子關係:

    除了 document 之外,每一個節點都會有上層與下層的關係,上層稱為「父節點 ( Parent node ) 」,下曾則是「子節點 ( Child node ) 」。

  • 兄弟關係:

    故名思義就是同一個父親底下的產物(?),擁有同一個「父節點」的節點,他們的關係就是「兄弟節點 ( Siblings node ) 」。

父關係 API

  • parentNode / 每一個節點都會有一個 parentNode 屬性,它表示元素的父節點;我們可以透過 Node.parentNode 來取得父元素,回傳的值可能會是元素節點 ( Element )、根節點 ( Document ) 或 DocumentFragment 節點。

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var li = document.querySelector('li');
      console.log(li.parentNode.nodeName); // "ul"
    </script>
    
  • parentElement / 回傳當前節點的父元素節點,如果該元素沒有父節點或者父節點不是一個 DOM 元素,則回傳 null。

子關係 API

  • childNodes / 回傳一個即時的 NodeList,表示元素的子節點列表,子節點可能會包含文本節點,註釋節點等,這邊我們可以透過 Node.hasChildNodes() 來檢查看看 DOM 節點是否有子節點:

    var node = document.querySelector('#hi');
    
    // 如果 node 內有子元素
    if( node.hasChildNodes() ) {
    
        // 可以透過 node.childNodes[n] (n 為數字索引) 取得對應的節點
        // 注意,NodeList 物件內容為即時更新的集合
        for (var i = 0; i < node.childNodes[i].length; i++) {
          // ...
        };
    }
    

    Node.childNodes 可能會回傳的有:

    • HTML 元素節點 (element nodes)
    • 文字節點 (text nodes)、文字間的空白
    • 註解節點 (comment nodes)
  • firstChild / 取得 node 第一個子節點,如果沒有子節點,則回傳 null

    底下可以看到子節點也會抓取空白行,因此這裡抓到的會是 ulli 中間的空白行,所以回傳 undefined

    <ul>
    	<li>text 1</li>
    	<li>text 2</li>
    	<li>text 3</li>
    </ul>
    
    <script>
    	var ul = document.querySelector('ul');
    
    	// tagName 屬性可以取得 node 的標籤名稱
    	console.log(ul.firstChild.tagName); // undefined
    </script>
    

    如果我們改成這樣:

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
    	var ul = document.querySelector('ul');
    	console.log(ul.firstChild.tagName); // LI
    </script>
    

    少了空白行就可以正確抓取我們要的第一個子節點。

  • lastChild / 取得 node 最後一個子節點,如果沒有子節點,則回傳 null。

    <ul>
    	<li>text 1</li>
    	<li>text 2</li>
    	<li>text 3</li>
    </ul>
    
    <script>
      var ul = document.querySelector('ul');
    
      // textContent 屬性可以取得節點內的文字內容
      console.log(ul.lastChild.textContent); // '\n\t' (換行字元)
    </script>
    

    這邊跟 firstChild ****的情況一樣,所以我們同樣改成底下範例,把空行去掉:

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var ul = document.querySelector('ul');
      console.log(ul.lastChild.textContent); // 'text 3'
    </script>
    

    也成功抓取到我們要的最後一個子節點。

  • hasChildNodes / 這個方法會回傳一個布林值,用途在上面有說到,他替我們抓取當前是否含有子節點。

    底下給一個範例,如果 id 為 a 的這個元素有子節點,則從 DOM 中刪除第一個子節點:

    var a = document.getElementById("a");
    
    if ( foo.hasChildNodes() ) {
      foo.removeChild(a.childNodes[0]);
    }
    

兄弟關係 API

  • previousSibling / 回傳當前節點的前一個兄弟節點,沒有則回傳 null

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var li = document.querySelector('li');
    	// 這裡已經沒有前一個兄弟節點
      console.log(li.previousSibling); // null
    
    	// document.querySelectorAll 會取得所有條件符合的元素
    	// document.querySelectorAll('span')[2] 指的是「第三個」符合條件元素
    	var li2 = document.querySelectorAll('li')[2];
      console.log(li2.previousSibling.textContent); // 'text 2'	
    </script>
    
  • nextSibling / 回傳當前節點的下一個兄弟節點,沒有則回傳 null

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var li = document.querySelector('li');
    	// 這裡直接抓取 text 1 的下一個兄弟節點
      console.log(li.nextSibling.textContent); // 'text 2'
    </script>
    

參考資料:

重新認識 JavaScript: Day 12 透過 DOM API 查找節點
JavaScript操作DOM常用的API


上一篇
JavaScript Day 23. flatMap()
下一篇
JavaScript Day 25. DOM Node 常用方法
系列文
我的JavaScript日常31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言