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 節點
通過上文中的一個圖片(全文中好像只有一張圖)我們可以看到,在獲取節點或者節點列表中出現了兩種不同的結果,分別是 NodeList
和 HTMLCollection
這是兩種不同的集合,成員分別是 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等)和方法,還有一些特有的屬性和方法,在此不過多贅述