溫故知新——DOM操作之獲取節點

DOM 是 JavaScript 操作網頁的接口,全稱爲“文檔對象模型”(Document Object Model)。它的作用是將網頁轉爲一個 JavaScript 對象,從而可以用腳本進行各種操作

針對節點進行操作的第一步當然就是獲取節點,目前已經衍生出了很多獲取節點的方案

我們先寫一段簡單的 HTML 代碼,

<body>
  <header id="title" class="main-title">
    <span class="text-heading">Hello,</span>
    <span class="text-heading name">Raaabbit</span>
  </header>
</body>

後面的代碼就基於上面的 demo 展開

針對節點進行操作的第一步當然就是獲取節點,目前已經衍生出了很多獲取節點的方案

通過元素選擇器獲取

元素選擇器相關的內容可以參考我的博客,CSS選擇器一節

這種方法主要在於通過元素的 id、class 和標籤名獲取元素,常用以下三個方法:

  • getElementById()
  • getElementsByClassName()
  • getElementsByTagName()

⚠️注意: 這些方法既可以用於HTML文檔對象,也可以用於element元素對象

下面分別進行詳細介紹:

getElementById

let title = document.getElementById('title');

按照規範在 HTML 中,id 是唯一的,所以通過這樣的方式可以獲得唯一的元素,事實上若瀏覽器中出現多個id名的情況,CSS樣式對所有該id名的元素都生效,但這個方法僅對第一個出現該id名的元素生效

getElementsByClassName

let textHeadings = document.getElementsByClassName('text-heading');
console.log(textHeadings[1]);

我們可以很容易通過這個方法名看出來,elements 就可以體現出,這個方法獲得的不是單個節點,而是一個類數組對象 HTMLCollection,我們可以通過下標訪問其中的每一個節點

⚠️注意: 我們可以用多個類名作爲參數,類名的先後順序不重要,如:

// 只有同時有類名 text-heading 和 name 時纔可以獲取到
let textHeadings = document.getElementsByClassName('text-heading name');

由於這個方法不支持 IE9 以下瀏覽器,所以需要使用兼容的寫法:

function getElementsByClassName(node,className){
  if(node.getElementsByClassName){                    //特性偵測
    return node.getElementsByClassName(className);  //優先使用W3C規範
  }
  else{
    let results = new Array();
    let elems = node.getElementsByTagName("*");     //獲取所有後代元素
    for(let i = 0;i < elems.length;i++){
      if(elems[i].className.indexOf(classname) != -1){
        results[results.length] = elems[i];     //擴容
      }
    }
    return results;
  }
}

getElementsByTagName

在上面給出的兼容方法中已經使用到了這個方法,getElementsByTagName 方法接收一個參數,即要取得元素的標籤名,返回的也是類數組對象 HTMLCollection

let spans = document.getElementsByTagName('span');

通過 selector 獲取

相信很多人都使用或者學習過 JQuery,其中有一個很方便的獲取元素節點的方法 $,比如我們可以通過 $('.text-heading') 這樣的方式快速獲取節點,這裏的節點是包裝後的 JQuery 對象,並不是原生的 DOM 節點

而 HTML5 中添加了新的方法,通過 CSS 選擇器更加靈活地獲取節點

querySelector

querySelector 方法接收一個 CSS 選擇器,返回與該模式匹配的第一個元素,如果沒有找到匹配的元素,返回null

該方法既可用於文檔document類型,也可用於元素element類型

let body = document.querySelector("body");

querySelectorAll

querySelectorAll 同樣接收一個CSS選擇符,和上一個方法的區別在於返回一個類數組對象 NodeList

let spans = document.querySelectorAll(".text-heading");

這兩個方法有一個很重要的特點,他們的返回值不是動態的,更像是一個快照,我們先調用 querySelectorAll 方法獲取節點後,對相關的節點進行添加/刪除/修改操作後,這個值並不改變:

// 本文最上方的例子
let spans = document.querySelectorAll("span");
console.log(spans.length); // 2
let newSpan = document.createElement('span');
document.getElementById('title').appendChild(newSpan);

console.log(spans.length); // 2,實際上現在已經有 3 個 span了

通過節點關係獲取

有的時候我們不僅需要對節點進行操作,還需要連帶操作它的父子/兄弟節點,我們也可以調用以下方法;

  • 父子關係:
    • parentNode/parentElement
    • firstChild/lastChild
    • childNodes/childern
  • 兄弟關係
    • previousSibling/nextSibling
    • previousElementSibling/nextElementSibling

使用例子如下:

let title = document.getElementsByClassName('main-title')[0];
console.log("firstChild ==> ", title.firstChild);
console.log("firstElementChild ==> ", title.firstElementChild);
console.log("childNodes ==> ", title.childNodes);
console.log("children ==> ", title.children);
let textHeading0 = document.getElementsByClassName('text-heading')[0];
console.log("parentElement ==> ", textHeading0.parentElement);
console.log("parentNode ==> ", textHeading0.parentNode);
console.log("nextSibling ==> ", textHeading0.nextSibling);
console.log("nextElementSibling ==> ", textHeading0.nextElementSibling);

結果如下:

由於隨着目前前端的交互性越來越強,頁面上元素變化較大,這些方法有比較大的侷限性,可維護性比較差

從另一個角度來說,有時我們不知道要獲取的元素的 id、class 甚至不知道 tag,通過節點關係獲取元素也是一種不錯的方案

總結

本文中列出的一些方法特性如下,多種方法結合使用效果更佳~

name only document sole(唯一的) live(動態的)
getElementById * *
getElementsByTagName *
getElementsByClassName *
querySelectorAll
querySelector *

擴展內容 Node 節點和 Element 節點

通過上文中的一個圖片(全文中好像只有一張圖)我們可以看到,在獲取節點或者節點列表中出現了兩種不同的結果,分別是 NodeListHTMLCollection 這是兩種不同的集合,成員分別是 Node
節點和 Element 節點

NodeList 實例對象是一個類數組對象,它的成員是節點對象,包括childNodes和querySelectorAll()方法返回值

HTMLCollection 集合包括getElementsByTagName()、getElementsByClassName()、getElementsByName()等方法的返回值,以及children

這兩種集合的主要區別在於是否具有動態性(見上文)

Node 和 Element 具有繼承關係,Element 繼承了 Node 接口,因此Element 上具有 Node 的屬性和方法

除了 element 之外 node 還包括很多類型,列舉如下;

  • 元素節點:ELEMENT
  • 屬性節點:ATTRIBUTE
  • 文本節點:TEXT
  • CDATA節點:CDATA_SECTION
  • 實體引用名稱節點:ENTRY_REFERENCE
  • 實體名稱節點:ENTITY
  • 處理指令節點:PROCESSING_INSTRUCTION
  • 註釋節點:COMMENT
  • 文檔節點:DOCUMENT
  • 文檔類型節點:DOCUMENT_TYPE
  • 文檔片段節點:DOCUMENT_FRAGMENT
  • DTD聲明節點:NOTATION

這些不同的 node 類型具有共同的屬性(nodeType、nodeName、nodeValue等)和方法,還有一些特有的屬性和方法,在此不過多贅述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章