DOM程式設計之API總結篇
原文 連結
一、基本型別介紹
1.1 Node型別
DOM1
級定義了一個Node
介面,該介面由DOM
中所有節點型別實現。這個Node
介面在JS
中是作為Node
型別實現的。在IE9
以下版本無法訪問到這個型別,JS
中所有節點都繼承自Node
型別,都共享著相同的基本屬性和方法Node
有一個屬性nodeType
表示Node
的型別,它是一個整數,其數值分別表示相應的Node
型別
- 假設我們要判斷一個
Node
是不是元素,我們可以這樣判斷
if(someNode.nodeType == 1){
console.log("Node is a element" );
}
- 這些
Node
型別中,我們最常用的就是element
,text
,attribute
,comment
,document
,document_fragment
這幾種型別
1.2 Element型別
Element
提供了對元素標籤名,子節點和特性的訪問,我們常用HTML
元素比如div
,span
,a
等標籤就是element
中的一種。Element
有下面幾條特性:nodeType
為1
nodeName
為元素標籤名,tagName
也是返回標籤名nodeValue
為null
parentNode
可能是Document
或Element
- 子節點可能是
Element
,Text
Comment
,Processing_Instruction
,CDATASection
或EntityReference
1.3 Text型別
Text
表示文字節點,它包含的是純文字內容,不能包含html
程式碼,但可以包含轉義後的html
程式碼。Text
有下面的特性:
nodeType
為3
nodeName
為#text
nodeValue
為文字內容parentNode
是一個Element
- 沒有子節點
1.4 Attr型別
Attr
型別表示元素的特性,相當於元素的attributes
屬性中的節點,它有下面的特性:
nodeType
值為2nodeName
nodeValue
是特性的值parentNode
為null
1.5 Comment型別
Comment
表示HTML
文件中的註釋,它有下面的幾種特徵:
nodeType
為8nodeName
為#comment
nodeValue
為註釋的內容parentNode
可能是Document
或Element
- 沒有子節點
1.6 Document
Document
表示文件,在瀏覽器中,document
物件是HTMLDocument
的一個例項,表示整個頁面,它同時也是window
物件的一個屬性。Document
有下面的特性:
nodeType
為9
nodeName
為#document
nodeValue
為null
parentNode
為null
- 子節點可能是一個
DocumentType
或Element
1.7 DocumentFragment型別
DocumentFragment
是所有節點中唯一一個沒有對應標記的型別,它表示一種輕量級的文件,可能當作一個臨時的倉庫用來儲存可能會新增到文件中的節點。DocumentFragment
有下面的特性:nodeType
為11
nodeName
為#document-fragment
nodeValue
為null
parentNode
為null
我們簡單地介紹了幾種常見的
Node
型別,要記住,HTML
中的節點並不只是包括元素節點,它還包括文字節點,註釋節點等等。在這裡我們只是簡單地說明了幾種常見的節點.
二、 DOM提供的幾個屬性
2.1 childNodes屬性
- 在一棵節點樹上,
childNodes
屬性可以用來獲取任何一個元素的所有子節點,它是一個包含這個元素全部子元素的陣列
element.childNodes
2.2 nodeType屬性
- 節點之間的關係構成了節點層次,
html
頁面的可以畫出一個以html
標籤為根節點的樹形結構
DOM
會把文件看作是一棵樹,同時定義了很多方法來操作這棵數中的每一個元素(節點)
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<p>hello world!!!</p>
</body>
</html>
- 每一個節點都有
nodeType
屬性
node.nodeType
nodeType
屬性總共有12中可能取值,但其中僅有3種有實用價值
- 元素節點的
nodeType
屬性值是1 - 屬性節點的
nodeType
屬性值是2 - 文字節點的
nodeType
屬性值是3
- 元素節點的
description.firstChild.nodeValue = text;
2.3 nodeValue屬性
nodeValue
屬性
- 如果想改變一個文字節點的值,那就使用
DOM
提供的nodeValue
,它用來得到一個節點的值node.nodeValue
- 需要注意:
nodeValue
屬性獲取物件的值時,得到的並不是包含在這個段落裡的文字 - nodeValue屬性不僅可以用來檢測節點的值,還可以設定節點的值
- 如果想改變一個文字節點的值,那就使用
2.4 firstChild和lastChild屬性
- 陣列元素
childNodes[0]
有個更直觀的同義詞。無論如何,只要訪問childNodes
陣列的第一個元素,都可以把它寫成firstChild
node.firstChild
與下面等價
node.childNodes[0]
DOM
還提供了一個與之對應的lastChild
屬性
node.lastChild
### 三、節點建立型API
- 在這裡,我將常用的
DOM
操作api
進行分類,首先要介紹的是建立型的api
。這一型別的api
,簡而言之就是用來建立節點的
3.1 createElement
createElement
通過傳入指定的一個標籤名來建立一個元素,如果傳入的標籤名是一個未知的,則會建立一個自定義的標籤,注意:IE8
以下瀏覽器不支援自定義標籤
var div = document.createElement("div");
- 使用
createElement
要注意:通過createElement
建立的元素並不屬於html
文件,它只是創建出來,並未新增到html
文件中,要呼叫appendChild
或insertBefore
等方法將其新增到HTML
文件樹中
3.2 createTextNode
createTextNode
用來建立一個文字節點,用法如下
var textNode = document.createTextNode("一個TextNode");
createTextNode
接收一個引數,這個引數就是文字節點中的文字,和createElement
一樣,建立後的文字節點也只是獨立的一個節點,同樣需要appendChild
將其新增到HTML
文件樹中
3.3 cloneNode
cloneNode
是用來返回呼叫方法的節點的一個副本,它接收一個bool
引數,用來表示是否複製子元素,使用如下:
var parent = document.getElementById("parentElement");
var parent2 = parent.cloneNode(true);// 傳入true
parent2.id = "parent2";
- 這段程式碼通過
cloneNode
複製了一份parent
元素,其中cloneNode
的引數為true
,表示parent
的子節點也被複制,如果傳入false
,則表示只複製了parent
節點
<div id="parent">
我是父元素的文字
<br/>
<span>
我是子元素
</span>
</div>
<button id="btnCopy">複製</button>
var parent = document.getElementById("parent");
document.getElementById("btnCopy").onclick = function(){
var parent2 = parent.cloneNode(true);
parent2.id = "parent2";
document.body.appendChild(parent2);
}
這段程式碼很簡單,主要是繫結
button
事件,事件內容是複製了一個parent
,修改其id
,然後新增到文件中這裡有幾點要注意:
- 和
createElement
一樣,cloneNode
建立的節點只是遊離有html
文件外的節點,要呼叫appendChild
方法才能新增到文件樹中 - 如果複製的元素有
id
,則其副本同樣會包含該id
,由於id
具有唯一性,所以在複製節點後必須要修改其id - 呼叫接收的
bool
引數最好傳入,如果不傳入該引數,不同瀏覽器對其預設值的處理可能不同
- 和
除此之外,我們還有一個需要注意的點:
- 如果被複制的節點綁定了事件,則副本也會跟著繫結該事件嗎?這裡要分情況討論:
- 如果是通過
addEventListener
或者比如onclick
進行繫結事件,則副本節點不會繫結該事件 - 如果是內聯方式繫結比如
- 如果是通過
- 如果被複制的節點綁定了事件,則副本也會跟著繫結該事件嗎?這裡要分情況討論:
<div onclick="showParent()"></div>
- 這樣的話,副本節點同樣會觸發事件
3.4 createDocumentFragment
createDocumentFragment
方法用來建立一個DocumentFragment
。在前面我們說到DocumentFragment
表示一種輕量級的文件,它的作用主要是儲存臨時的節點用來準備新增到文件中createDocumentFragment
方法主要是用於新增大量節點到文件中時會使用到。假設要迴圈一組資料,然後建立多個節點新增到文件中
<ul id="list"></ul>
<input type="button" value="新增多項" id="btnAdd" />
document.getElementById("btnAdd").onclick = function(){
var list = document.getElementById("list");
for(var i = 0;i < 100; i++){
var li = document.createElement("li");
li.textContent = i;
list.appendChild(li);
}
}
這段程式碼將按鈕綁定了一個事件,這個事件建立了100個
li
節點,然後依次將其新增HTML
文件中。這樣做有一個缺點:每次一建立一個新的元素,然後新增到文件樹中,這個過程會造成瀏覽器的迴流。所謂迴流簡單說就是指元素大小和位置會被重新計算,如果新增的元素太多,會造成效能問題。這個時候,就是使用createDocumentFragment了
DocumentFragment
不是文件樹的一部分,它是儲存在記憶體中的,所以不會造成迴流問題。我們修改上面的程式碼如下
document.getElementById("btnAdd").onclick = function(){
var list = document.getElementById("list");
var fragment = document.createDocumentFragment();
for(var i = 0;i < 100; i++){
var li = document.createElement("li");
li.textContent = i;
fragment.appendChild(li);
}
list.appendChild(fragment);
}
- 優化後的程式碼主要是建立了一個
fragment
,每次生成的li
節點先新增到fragment
,最後一次性新增到list
3.5 建立型API總結
建立型
api
主要包括createElement
,createTextNode
,cloneNode
和createDocumentFragment
四個方法,需要注意下面幾點:- 它們建立的節點只是一個孤立的節點,要通過
appendChild
新增到文件中 cloneNode
要注意如果被複制的節點是否包含子節點以及事件繫結等問題- 使用
createDocumentFragment
來解決新增大量節點時的效能問題
- 它們建立的節點只是一個孤立的節點,要通過
四、頁面修改型API
- 前面我們提到建立型
api
,它們只是建立節點,並沒有真正修改到頁面內容,而是要呼叫appendChild
來將其新增到文件樹中。我在這裡將這類會修改到頁面內容歸為一類。
修改頁面內容的api
主要包括:appendChild
,insertBefore
,removeChild
,replaceChild
4.1 appendChild
appendChild
我們在前面已經用到多次,就是將指定的節點新增到呼叫該方法的節點的子元素的末尾。呼叫方法如下:
parent.appendChild(child);
child
節點將會作為parent
節點的最後一個子節點appendChild
這個方法很簡單,但是還有有一點需要注意:如果被新增的節點是一個頁面中存在的節點,則執行後這個節點將會新增到指定位置,其原本所在的位置將移除該節點,也就是說不會同時存在兩個該節點在頁面上,相當於把這個節點移動到另一個地方
<div id="child">
要被新增的節點
</div>
<br/>
<br/>
<br/>
<div id="parent">
要移動的位置
</div>
<input id="btnMove" type="button" value="移動節點" />
document.getElementById("btnMove").onclick = function(){
var child = document.getElementById("child");
document.getElementById("parent").appendChild(child);
}
- 這段程式碼主要是獲取頁面上的
child
節點,然後新增到指定位置,可以看到原本的child
節點被移動到parent
中了。
這裡還有一個要注意的點:如果child
綁定了事件,被移動時,它依然繫結著該事件
4.2 insertBefore
insertBefore
用來新增一個節點到一個參照節點之前,用法如下
parentNode.insertBefore(newNode,refNode);
parentNode
表示新節點被新增後的父節點newNode
表示要新增的節點refNode
表示參照節點,新節點會新增到這個節點之前
<div id="parent">
父節點
<div id="child">
子元素
</div>
</div>
<input type="button" id="insertNode" value="插入節點" />
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").onclick = function(){
var newNode = document.createElement("div");
newNode.textContent = "新節點"
parent.insertBefore(newNode,child);
}
- 這段程式碼建立了一個新節點,然後新增到
child
節點之前 和
appendChild
一樣,如果插入的節點是頁面上的節點,則會移動該節點到指定位置,並且保留其繫結的事件。關於第二個引數參照節點還有幾個注意的地方:
refNode
是必傳的,如果不傳該引數會報錯- 如果
refNode
是undefined
或null
,則insertBefore
會將節點新增到子元素的末尾
4.3 removeChild
removeChild
顧名思義,就是刪除指定的子節點並返回,用法如下
var deletedChild = parent.removeChild(node);
deletedChild
指向被刪除節點的引用,它等於node
,被刪除的節點仍然存在於記憶體中,可以對其進行下一步操作。注意:如果被刪除的節點不是其子節點,則程式將會報錯。我們可以通過下面的方式來確保可以刪除:
if(node.parentNode){
node.parentNode.removeChild(node);
}
- 通過節點自己獲取節點的父節點,然後將自身刪除
4.4 replaceChild
replaceChild
用於使用一個節點替換另一個節點,用法如下
parent.replaceChild(newChild,oldChild);
newChild
是替換的節點,可以是新的節點,也可以是頁面上的節點,如果是頁面上的節點,則其將被轉移到新的位置oldChild
是被替換的節點
4.5 頁面修改型API總結
- 頁面修改型api主要是這四個介面,要注意幾個特點:
- 不管是新增還是替換節點,如果新增或替換的節點是原本存在頁面上的,則其原來位置的節點將被移除,也就是說同一個節點不能存在於頁面的多個位置
- 節點本身繫結的事件會不會消失,會一直保留著
五、節點查詢型API
- 節點查詢型
API
也是非常常用的
5.1 document.getElementById
這個介面很簡單,根據元素
id
返回元素,返回值是Element
型別,如果不存在該元素,則返回null
使用這個介面有幾點要注意:
- 元素的
Id
是大小寫敏感的,一定要寫對元素的id
HTML
文件中可能存在多個id
相同的元素,則返回第一個元素- 只從文件中進行搜尋元素,如果建立了一個元素並指定
id
,但並沒有新增到文件中,則這個元素是不會被查詢到的
- 元素的
5.2 document.getElementsByTagName
- 這個介面根據元素標籤名獲取元素,返回一個即時的
HTMLCollection
型別,什麼是即時的HTMLCollection
型別呢?
<div>div1</div>
<div>div2</div>
<input type="button" value="顯示數量" id="btnShowCount"/>
<input type="button" value="新增div" id="btnAddDiv"/>
var divList = document.getElementsByTagName("div");
document.getElementById("btnAddDiv").onclick = function(){
var div = document.createElement("div");
div.textContent ="div" + (divList.length+1);
document.body.appendChild(div);
}
document.getElementById("btnShowCount").onclick = function(){
alert(divList.length);
}
這段程式碼中有兩個按鈕,一個按鈕是顯示
HTMLCollection
元素的個數,另一個按鈕可以新增一個div標籤到文件中。前面提到HTMLCollcetion
元素是即時的表示該集合是隨時變化的,也就是是文件中有幾個div
,它會隨時進行變化,當我們新增一個div
後,再訪問HTMLCollection
時,就會包含這個新增的div
使用document.getElementsByTagName這個方法有幾點要注意:
- 如果要對
HTMLCollection
集合進行迴圈操作,最好將其長度快取起來,因為每次迴圈都會去計算長度,暫時快取起來可以提高效率 - 如果沒有存在指定的標籤,該介面返回的不
是null
,而是一個空的HTMLCollection
“*”
表示所有標籤
- 如果要對
5.3 document.getElementsByName
getElementsByName
主要是通過指定的name
屬性來獲取元素,它返回一個即時的NodeList
物件。一般用於獲取表單元素的·name·屬性使用這個介面主要要注意幾點:
- 返回物件是一個即時的
NodeList
,它是隨時變化的 - 在
HTML
元素中,並不是所有元素都有name
屬性,比如div
是沒有name
屬性的,但是如果強制設定div的
name`屬性,它也是可以被查詢到的 - 在
IE
中,如果id
設定成某個值,然後傳入getElementsByName
的引數值和id
值一樣,則這個元素是會被找到的,所以最好不好設定同樣的值給id
和name
- 返回物件是一個即時的
5.4 document.getElementsByClassName
- 這個
API
是根據元素的class
返回一個即時的HTMLCollection
,用法如下
var elements = document.getElementsByClassName(names);
- 這個介面有下面幾點要注意:
- 返回結果是一個即時的
HTMLCollection
,會隨時根據文件結構變化 IE9
以下瀏覽器不支援- 如果要獲取
2
個以上classname
,可傳入多個classname
,每個用空格相隔,例如
- 返回結果是一個即時的
var elements = document.getElementsByClassName("test1 test2");
5.5 document.querySelector和document.querySelectorAll
這兩個
api
很相似,通過css
選擇器來查詢元素,注意選擇器要符合CSS
選擇器的規則首先來介紹一下
document.querySelector
document.querySelector
返回第一個匹配的元素,如果沒有匹配的元素,則返回null
。- 注意,由於返回的是第一個匹配的元素,這個
api
使用的深度優先搜尋來獲取元素
<div>
<div>
<span class="test">第三級的span</span>
</div>
</div>
<div class="test">
同級的第二個div
</div>
<input type="button" id="btnGet" value="獲取test元素" />
document.getElementById("btnGet").addEventListener("click",function(){
var element = document.querySelector(".test");
alert(element.textContent);
})
這個例子很簡單,就是兩個
class
都包含“test”
的元素,一個在文件樹的前面,但是它在第三級,另一個在文件樹的後面,但它在第一級,通過querySelector
獲取元素時,它通過深度優先搜尋,拿到文件樹前面的第三級的元素document.querySelectorAll
的不同之處在於它返回的是所有匹配的元素,而且可以匹配多個選擇符
<div class="test">
class為test
</div>
<div id="test">
id為test
</div>
<input id="btnShow" type="button" value="顯示內容" />
document.getElementById("btnShow").addEventListener("click",function(){
var elements = document.querySelectorAll("#test,.test");
for(var i = 0,length = elements.length;i<length;i++){
alert(elements[i].textContent);
}
})
這段程式碼通過
querySelectorAll
,使用id
選擇器和class
選擇器選擇了兩個元素,並依次輸出其內容。要注意兩點:querySelectorAll
也是通過深度優先搜尋,搜尋的元素順序和選擇器的順序無關- 返回的是一個非即時的
NodeList
,也就是說結果不會隨著文件樹的變化而變化
相容性問題:
querySelector
和querySelectorAll
在ie8
以下的瀏覽器不支援小結:
document.getElementById
返回一個物件document.getElementsByName
和document.getElementsByClasName
返回一個物件陣列
六、節點關係型API
- 在
html
文件中的每個節點之間的關係都可以看成是家譜關係,包含父子關係,兄弟關係等等
6.1 父關係型API
parentNode
:每個節點都有一個parentNode
屬性,它表示元素的父節點。Element
的父節點可能是Element
,Document
或DocumentFragment
parentElement
:返回元素的父元素節點,與parentNode
的區別在於,其父節點必須是一個Element
,如果不是,則返回null
6.2 兄弟關係型API
previousSibling
:節點的前一個節點,如果該節點是第一個節點,則為null
。注意有可能拿到的節點是文字節點或註釋節點,與預期的不符,要進行處理一下previousElementSibling
:返回前一個元素節點,前一個節點必須是Element
,注意IE9
以下瀏覽器不支援nextSibling
previousSibling
存在相容性問題nextElementSibling
previousElementSibling
推薦使用
<script>
var oUl = document.getElementById('ul1');
var firstEle = oUl.firstElementChild;
var lastEle = oUl.lastElementChild;
firstEle.nextElementSibling.style.background = 'red';
lastEle.previousElementSibling.style.background = 'green';
</script>
nextSibling
:節點的後一個節點,如果該節點是最後一個節點,則為null
。注意有可能拿到的節點是文字節點,與預期的不符,要進行處理一下nextElementSibling
:返回後一個元素節點,後一個節點必須是Element
,注意IE9
以下瀏覽器不支援
6.3 子關係型API
childNodes
:
- 返回一個即時的
NodeList
,表示元素的子節點列表,子節點可能會包含文字節點,註釋節點等。childNodes
子節點列表集合(只讀屬性 有相容性問題 )
- 返回一個即時的
<ul id="ul1">
<li>11111</li>
<li>22222</li>
<li>3333</li>
<li>44444</li>
</ul>
<script>
var oUl = document.getElementById('ul1');
console.log(oUl.childNodes);
</script>
從截圖中可以看出 這段程式碼中
ul
的子節點有9
個,這說明使用childNodes
獲取的節點包括了文字節點和元素節點childNodes
在低版本的ie
瀏覽器下獲取的節點只包括元素節點,這就導致了相容性問題- 如何解決相容性??
- 根據子節點的
nodeType
屬性值判斷
- 根據子節點的
for (var i=0; i<oUl.childNodes.length; i++) {
if ( oUl.childNodes[i].nodeType == 1 ) {
oUl.childNodes[i].style.background = 'red';
}
}
children
:- 一個即時的
HTMLCollection
,子節點都是Element
,IE9
以下瀏覽器不支援。children
子節點列表集合(只讀屬性 推薦使用 )
- 一個即時的
children
獲取的子節點只包含元素節點
for (var i = 0; i<oUl.children.length; i++){
oUl.children[i].style.background = 'red';
}
firstNode
:第一個子節點lastNode
:最後一個子節點firstChild
(firstElementChild)lastChild
(lastElementChild) 第一個子節點 最後一個子節點firstChild
、lastChild
和childNodes
同樣的存在相容性問題,在低版本ie
瀏覽器中只能獲取到元素節點firstElementChild
、lastElementChild
獲取第一個元素子節點,最後一個元素子節點 推薦使用
var oUl = document.getElementById('ul1');
// oUl.firstChild.style.background = 'red';//標準瀏覽器 報錯
// oUl.lastChild.style.background = 'red';//標準瀏覽器 報錯
oUl.firstElementChild.style.background = 'red';
oUl.lastElementChild.style.background = 'red';
hasChildNodes
方法:可以用來判斷是否包含子節點
七、元素屬性型
7.1 setAttribute
setAttribute
:它允許我們對元素屬性值做出修改與getAttribute
一樣setAttribute
也能用於元素節點- 通過
setAttribute
對文件做出修改後,在通過瀏覽器的檢視原始碼選項看到的任然是改變之前的屬性值,也就是說setAttribue
做出的修改,不會反應到文件本身的原始碼裡 setAttribute
優勢在於可以修改文件中的任何一個屬性
element.setAttribute(name, value);
- 其中
name
是特性名,value
是特性值。如果元素不包含該特性,則會建立該特性並賦值。 - 如果元素本身包含指定的特性名為屬性,則可以訪問屬性進行賦值,比如下面兩條程式碼是等價
element.setAttribute("id","test");
element.id = "test";
非DOM的解決方案
- 其實不用
setAttribute
也可以改變元素的屬性 setAttribute
方法是第一級DOM
的組成部分,它可以設計任何元素節點的任意屬性。在第1級DOM
出現之前,你可以通過另外一種辦法設定大部分元素的屬性
- 其實不用
element.value = "the new value";
與下面語句等價
element.setAttribute("value","the new value");
7.2 getAttribute
getAttribute
返回指定的特性名相應的特性值,如果不存在,則返回null
或空字串。getAttribute
不屬性doucment
物件,不能通過document
物件呼叫,只能通過元素節點物件呼叫- 例如可以與
getElementsByTagName
方法合用,獲取每個p
的title
屬性
var paras = document.getElementsByTagName("p");
for(var i=0;i<para.lenght;i++){
alert(paras.getAttrtitube("title"));
}
八、表格操作
九、樣式操作
十、大小和偏移
十一、網上的一張思維導圖總結
- 參考
JavaScript DOM
程式設計藝術