[JavaScript] DOM

1. Node類型


1.1 什麼是DOM

DOM是針對HTML和XML文檔的一個API,它描繪了一個層次化的節點樹,允許開發人員添加、移除和修改頁面的某一部分。節點分爲幾種不同的類型,每種類型分別表示文檔中不同的信息及標記,每個節點都擁有自己的特點、數據和方法,另外也與其他節點存在某種關係,節點之間的關係構成了層次,而所有頁面標記則表現爲一個以特定節點爲根節點的樹型結構。


1.2 Node

DOM1級定義了一個Node接口,該接口將由DOM中的所有節點類型實現。Node接口在JavaScript中是作爲Node類型實現的,除IE外,其他所有瀏覽器都可以訪問到這個類型。JavaScript中的所有節點類型都繼承自Node類型,節點類型由在Node類型中定義的下列12個數值常量來表示:

1) Node.ELEMENT_NODE(1)

2) Node.ATTRIBUTE_NODE(2)

3) Node.TEXT_NODE(3)

4) Node.CDATA_SECTION_NODE(4)

5) Node.ENTITY_REFERENCE_NODE(5)

6) Node.ENTITY_NODE(6)

7) Node.PROCESSING_INSTRUCTION_NODE(7)

8) Node.COMMENT_NODE(8)

9) Node.DOCUMENT_NODE(9)

10) Node.DOCUMENT_TYPE_NODE(10)

11) Node.DOCUMENT_FRAGMENT_NODE(11)

12) Node.NOTATION_NODE(12)

通過比較上面這些常量,可以確定節點的類型,例如:

if (mynode.nodeType == Node.ELEMENT_NODE) {
  alert("Node is an element.");
}

但是IE沒有公開Node類型的構造函數,因此爲了兼容,最好使用數字比較,例如:

if (mynode.nodeType == 1) {
  alert("Node is an element.");
}

要了解節點的具體信息,可以使用nodeName和nodeValue兩個屬性,它們的值取決於節點的類型。對於元素節點,nodeName中保存的是元素的標籤名,而nodeValue爲null。

每個節點都有一個childNodes屬性,其中保存着一個NodeList對象。NodeList是一種類數組對象,用於保存一組有序的節點,可以通過位置來訪問這些節點。雖然可以通過方括號來訪問NodeList的值,而這個對象也有length屬性,但它並不是Array的實例,例如:

var firstChild = mynode.childNodes[0];
var secondChild = mynode.childNodes.item(1);
var count = someNode.childNodes.length;

每個節點都有一個parentNode屬性,該屬性指向文檔樹中的父節點。包含在childNodes列表中的每個節點都有同一個父節點,且相互之間都是同胞節點。通過使用列表中每個節點的previousSibling和nextSibling屬性,可以訪問同一列表中的其他節點。列表中第一個節點的previousSibling和最後一個節點的nextSibling屬性的值都是null。

hasChildNodes()方法在節點包含一或多個子節點時返回true,可以用以檢測節點是否包含子節點。所有節點都有一個屬性ownerDocument,該屬性指向表示整個文檔的文檔節點,通過這個屬性,我們可以不必在節點層次中回溯到達頂端,而是可以直接訪問文檔節點。


1.3 操作節點

DOM提供了一些操作節點的方法,最常用的是appendChild(),用於向childNodes列表的末尾添加一個節點,更新完成後,appendChild()返回新增的節點,例如:

var returnNode = mynode.appendChild(newNode);

如果傳入到appendChild()中的節點已經是文檔的一部分,那結果就會將該節點從原位置移到新位置。

如果需要把節點放在 childNodes列表中的某個特定的位置上,而不是末尾,可以使用insertBefore()方法。這個方法接受兩個參數:要插入的節點和作爲參照的節點。插入完成後,insertBefore()返回插入的節點,例如:

var returnNode = mynode.insertBefore(newNode, someNode);

如果參照節點是null,則insertBefore與appendChild()相同。

replaceChild()方法接受兩個參數:要插入的節點和要替換的節點。被替換的節點將由這個方法返回並從文檔樹中被移除,同時要插入的節點佔據其位置,例如:

var returnNode = mynode.replaceChild(newNode, someNode);

如果只要移除節點,可以使用removeChild()方法。這個方法接受一個參數,即要移除的節點,同時返回被移除的節點,例如:

var returnNode = mynode.removeChild(someNode);

以上四種方法操作的都是某個節點的子節點,要使用這幾個方法必須先取得父節點,如果在不支持子節點的節點上調用這些方法,會導致錯誤。

有兩個方法是所有類型的節點都有的。第一個是cloneNode(),用於創建調用這個方法的節點的一個完全相同的副本。cloneNode()方法接受一個布爾值參數,表示是否執行深複製,深複製即複製節點及其整個子節點樹。複製後返回的節點副本屬於文檔,但沒有父節點,尾須通過方法將它添加到文檔中。

還有一個方法是normalize(),這個方法處理文檔樹中的文本節點。由於某些原因,文本節點可能不包含文本,或者接連出現兩個文本節點。當在某個節點上調用這個方法時,就會在該節點的後代節點中查找這種情況,如果找到空文本節點,則刪除它,如果找到相鄰的文本節點,則合併它們。


2. Document類型


2.1 Document

JavaScript通過Document類型表示文檔。在瀏覽器上,document對象是HTMLDocument(繼承自Document類型)的一個實例,表示整個HTML頁面。頁且,document對象是window對象的一個屬性,因此可以將其作爲全局對象來訪問。Document節點具有以下特徵:

1) nodeType的值爲9。

2) nodeName的值爲"#document"。

3) nodeValue的值爲null。

4) parentNode的值爲null。

DOM標準規定Document節點的子節點可以是DocumentType、Element、ProcessingInstruction或Comment,還有一些內置的訪問其子節點的快捷方式。

第一個是documentElement屬性,它始終指向HTML頁面中的<html>元素。還有一個body屬性,指向<body>元素。所有瀏覽器都支持document.documentELement和document.body屬性。

通常將<!DOCTYPE>標籤看成一個與文檔其他部分不同的實體,可以通過doctype屬性(document.doctype)來訪問它的信息。不同瀏覽器對doctype的支持差異很大。

作爲HTMLDocument的一個實例,document對象還有一些標準的Document對象所沒有的屬性,這些屬性提供了document對象所表現的網頁的一些信息。其中第一個屬性是title,包含着<title>元素中的文本。還有三個與對網頁請求有關的屬性:URL、domain和referrer。URL屬性中包含頁面完整的URL,domain屬性只包含頁面的域名,而referrer屬性中則保存着鏈接到當前頁面的那個頁面的URL,在沒有來源頁面的情況下,referrer屬性中可能包含空字符串。

這三個屬性中只有domain屬性是可設置的,但由安全方面的限制,並非可以給domain設置任何值,如果URL中包含一個子域名,如www.test.com,那麼就只能將domain設置爲test.com。不能將這個屬性設置爲URL中不包含的域。瀏覽器對domain屬性還有一個限制,即如果域名一開始是“鬆散的”,那麼不能將它再設置爲“緊湊的”。例如將document.domain設置爲test.com後,就不能再將其設置回www.test.com,否則會導致錯誤。


2.2 查找元素

取得元素的操作可以使用document的幾個方法來完成,包括getElementById()和getElementsByTagName()等。

getElement()接受一個參數:要取得的元素的ID,如果找到相應元素則返回,否則返回null,例如:

var div = document.getElementById("mydiv");

注意ID必須與頁面中的元素id屬性嚴格匹配,包括大小寫。但IE8及較低版本不區分大小寫,如果頁面中多個元素的ID值相同,getElementById()只返回文檔中第一次出現的元素。IE7中如果表單元素的name屬性與給定的ID匹配,也會被該方法返回。

另一個常用於取得元素引用的方法是getElementsByTagName()。這個方法接受一個參數,即要取得元素的標籤名,而返回的是包含零或多個元素的集合,例如:

var images = document.getElementsByTagName("img");

這個方法會返回一個HTMLCollection對象,該對象與NodeList類似, 可以使用方括號語法或item()方法來訪問其中的項,而元素的數量可以通過其length屬性取得。HTMLCollection對象還有一個方法:namedItem(),使用這個方法可以通過元素的name屬性取得集合中的項,例如:

var myimage = images.namedItem("myImage");

在提供按索引訪問項的基礎上,HTMLCollection還支持按名稱訪問項,對命名的項也可以使用方括號來訪問。要取得所有的元素,可以向getElementsByTagName()中傳入“*”。

第三個方法,也是隻有HTMLDocument纔有的方法,是getElementsByName()。這個方法會返回帶有給定name屬性的所有元素,例如:

var radios = document.getElementsByName("color");

getElementsByName()也會返回一個HTMLCollection對象。


2.3 特殊集合

除了屬性和方法,document對象還有一些特殊的集合。這些集合都是HTMLCollection對象,爲訪問文檔常用的部分提供了快捷方式,包括以下:

1) document.anchors: 包含文檔中所有帶name屬性的<a>元素。

2) document.applets: 包含文檔中所有的<applet>元素。

3) document.forms: 包含文檔中所有的<form>元素。

4) document.images: 包含文檔中所有的<img>元素。

5) document.links: 包含文檔中所有帶href屬性的<a>元素。


2.4 一致性檢測

由於DOM分爲多個級別,因此檢測瀏覽器實現了DOM的哪些部分就十分必要。document.implementation屬性就爲此提供相應信息和功能。DOM1級只爲document.implementation規定了一個方法,即hasFeature()。這個方法接受兩個參數:要檢測的DOM功能的名稱及版本號,如果瀏覽器支持對應的名稱和版本,則返回true,例如:

var hasXMLDom = document.implementation.hasFeature("XML", "1.0");
可以檢測的不同的值及版本號
功能
版本號說明
Core1.0、2.0、3.0基本的DOM,用於描述表現文檔的節點樹。
XML1.0、2.0、3.0Core的XML擴展。
HTML1.0、2.0XML的HTML擴展。
Views2.0基於某些樣式完成文檔的格式化。
StyleSheets2.0將樣式表關聯到文檔。
CSS2.0對層疊樣式表1級的支持。
CSS22.0對層疊樣式表2級的支持。
Events2.0常規的DOM事件。
UIEvents2.0用戶界面事件。
MouseEvents2.0由鼠標引發的事件。
MutationEvents2.0DOM樹變化時引發的事件。
HTMLEvents2.0HTML4.01事件。
Range2.0用於操作DOM樹中某個節圍的對象和方法。
Traversal2.0遍歷DOM樹的方法。
LS3.0文件與DOM樹之間的同步加載和保存。
LS-Async3.0文件與DOM樹之間的異步加載和保存。
Validation3.0在確保有效的前提下修改DOM樹的方法。


2.5 文檔寫入

document對象擁有將輸出流寫入到網頁中的能力。這個能力體現在4個方法中:write()、writeln()、open()和close()。其中write()和writeln()方法都接受一個字符串參數,即要寫入到輸出流中的文本。write()會原樣寫入,而writeln()會在字符串末尾添加一個換行符,例如:

document.write("<strong>" + (new Date()).toString() + "</strong>");

此外,還可以使用write()和writeln()方法動態地包含外部資源,但是不能直接包含字符串"</script>",因爲這會導致該字符串被解釋爲腳本塊的結束,所以解決方案:

document.write("<script type=\"text/javascript\" src=\"file.js\"") + "</scr" + "ipt>";

如果在文檔結束後再調用document.write(),那麼在輸出的內容將會重寫整個頁面。


3. Element類型


3.1 Element

除了Document類型之外,Element類型是Web編輯中最常用的類型。Element類型用於表現XML或HTML元素,提供了對元素標籤名、子節點及屬性的訪問。Element節點具有以下特徵:

1) nodeType的值爲1。

2) nodeName的值爲元素的標籤名。

3) nodeValue的值爲null。

4) parentNode可能是Document或Element。

要訪問元素的標籤名,可以使用nodeName屬性,也可以使用tagName屬性,這兩個屬性會返回相同的值,例如:

var div = document.getElementById("mydiv");
alert(div.tagName);
alert(div.nodeName);

在HTML中,標籤名始終以大寫表示,而XML中,標籤名與源代碼中的保持一致,假如不確定自己的腳本會在HTML還是XML中執行,可以在比較之前轉換爲相同的大小寫。


3.2 HTML元素

所有HTML元素都由HTMLElement類型表示。HTMLElement類型直接繼承自Element並添加了一些屬性:

1) id: 元素在文檔中的唯一標識符。

2) title: 有關元素的附加說明信息。

3) lang: 元素內容的語言代碼。

4) dir: 語言的方向,值爲"ltr"或"rtl"。

5) className: 與元素的class屬性對應。

上述這些屬性都可以用來取得或修改相應的屬性值,例如:

var div = document.getElementById("mydiv"); 
alert(div.id); 
alert(div.className);


3.3 取得和設置屬性

每個元素都有一或多個屬性,操作屬性的DOM方法主要有三個:getAttribute()、setAttribute()和removeAttribute(),例如:

var div = document.getElementById("mydiv");  
alert(div.getAttribute("id"));  
alert(div.getAttribute("class"));

注意傳遞給getAttribute()的屬性名與實際的屬性名相同,比如class屬性值,只有在通過對象屬性訪問時才使用className。如果給定名稱的屬性不存在,則返回null。

通過getAttribute()方法也可以取得自定義屬性,屬性的名稱是不區分大小寫的。有兩類特殊的屬性,雖然有屬性名,但屬性的值與通過getAttribute()返回的值並不相同。第一類是style,在通過getAttribute()訪問時,返回的style屬性值是CSS文本,而通過屬性訪問則會返回一個對象。第二類是類似

與getAttribute()對應的方法是setAttribute(),這個方法接受兩個參數:要設置的屬性名和值,如果屬性已經存在,則會以指定的值替換現有的值,如果屬性不存在,則創建該屬性並設置相應的值,例如:

var div = document.getElementById("mydiv");   
div.setAttribute("id", "myid");
div.setAttribute("class", "myclass");

通過setAttribute()方法即可以操作HTML屬性也可以操作自定義屬性。通過這個方法設置的屬性名會被統一轉換爲小寫形式。

可以直接給屬性賦值,但不包括自定義屬性,例如:

var div = document.getElementById("mydiv");    
div.id = "myid";
div.class = "myclass";

removeAttribute()方法用於徹底刪除元素的屬性,調用這個方法不僅會清除屬性的值,也會從元素中完全刪除屬性,例如:

var div = document.getElementById("mydiv");     
div.removeAttribute("class");


3.4 attributes屬性

Element類型是使用attributes屬性的唯一一個DOM節點類型。attributes屬性中包含一個NamedNodeMap,與NodeList類似,也是一個動態集合。元素中的每一個屬性都由一個Attr節點表示,每個節點都保存在NamedNodeMap對象中,NamedNodeMap有以下方法:

1) getNamedItem(name): 返回nodeName屬性等於name的節點。

2) removeNamedItem(name): 從列表中移除nodeName屬性等於name的節點。

3) setNamedItem(node): 向列表中添加節點,以節點的nodeName屬性爲索引。

4) item(pos): 返回位於pos位置處的節點。

attributes屬性中包含一系列節點,每個節點的nodeName就是屬性的名稱,而節點的nodeValue就是屬性的值,例如:

var id = element.attributes.getNamedItem("id").nodeValue;
var id = element.attributes["id"].nodeValue;

調用removeNamedItem()直接刪除給定名稱的屬性,例如:

var oldAttr = element.attributes.removeNamedItem("id");

setNamedItem()方法可以爲元素添加一個屬性,需要傳入一個屬性節點,例如:

element.attributes.setNamedItem(newAttr);


3.5 創建元素

使用document.createElement()方法可以創建新元素。這個方法只接受一個參數,即要創建元素的標籤名。這個標籤名在HTML中不區分大小寫,而在XML中,則是區分大小寫的,例如:

var div = document.createElement("div");

在IE中可以用另一個方式使用createElement(),爲這個方法傳入完整的元素標籤,也可以包含屬性,例如:

var div = document.createElement("<div id=\"mydiv\"></div>");


3.6 元素的子節點

元素可以有任意數目的子節點和後代節點,元素的childNodes屬性中包含了它的所有子節點,這些子節點有可能是元素、文本節點、註釋或處理指令。不同瀏覽器在對待這些節點方面存在不同。

<ul id="mylist">
 <li>Item 1</li>
 <li>Item 2</li>
 <li>Item 3</li>
</ul>

在IE中解析,<ul>元素會有3個子節點,分別是3個<li>元素。但在其他瀏覽器中,<ul>元素都會有7個元素,包括3個<li>元素和4個文本節點(<li>元素之間的空白)。

元素也支持getElementsByTagName()方法,在通過元素調用這個方法時,除了搜索起點是當前元素,其他與通過document調用這個方法相同。


4. Text類型


4.1 Text

文本節點由Text類型表示,包含的是可以照字面解釋的純文本內容。純文本中可以包含轉義後的HTML字符,但不能包含HTML代碼。Text節點具有以下特徵:

1) nodeType的值爲3。

2) nodeName的值爲"#Text"。

3) nodeValue的值爲節點所包含的文本。

4) parentNode是一個Element。

5) 無子節點。

可以通過nodeValue屬性或data屬性訪問Text節點中包含的文本,這兩個屬性包含的值相同。使用以下方法可以操作節點中的文本:

1) appendData(text): 將text添加到節點的末尾。

2) deleteData(offset, count): 從offset指定的位置開始刪除count個字符。

3) insertData(offset, text): 在offset指定的位置插入text。

4) replaceData(offset, count, text): 用text替換從offset到offset+count處的文本。

5) splitText(offset): 從offset指定的位置將當前文本節點分成兩個文本節點。

6) substringData(offset, count): 提取從offset到offset+count處的字符串。

除此之外,Text節點還有一個length屬性,保存着節點中字符的數目,等同於nodeValue.length和data.length。

在默認情況下,每個可以包含內容的元素最多只能有一個文本節點,而且必須有內容。


4.2 創建文本節點

可以使用document.createTextNode()創建新文本節點,這個方法接受一個參數:要插入節點中的文本,例如:

var textNode = document.createTextNode("Hello World!");

一般每個元素只有一個文本節點,但在有些情況下可能包含多個文本節點,容易導致混亂,可以調用normalize()方法將相鄰的文本節點合併,這個方法是由Node類型定義的。Text類型的splitText()方法與normalize()相反,這個方法將一個文本節點分成兩個文本節點。


5. 其他類型


5.1 Comment類型

註釋在DOM中是通過Comment類型表示的,Comment節點具有以下特徵:

1) nodeType的值爲8。

2) nodeName的值爲"#comment"。

3) nodeValue的值是註釋的內容。

4) parentNode可能是Document或Element。

5) 沒有子節點。

Comment類型與Text類型繼承自相同的基類,因此它擁有除splitText()外所有字符串操作方法。與Text類型相似,可以通過nodeValue或data屬性來取得註釋的內容。


5.2 CDATASection類型

CDATASection類型只針對基於XML的文檔,表示的是CDATA區域。與Comment類似,CDATASection類型繼承自Text類型,因此擁有除splitText()外所有字符串操作方法。DATASection節點具有以下特徵:

1) nodeType的值爲4。

2) nodeName的值爲"#cdata-section"。

3) nodeValue的值爲CDATA區域中的內容。

4) parentNode可能是Document或Element。

5) 沒有子節點。

CDATA區域只會出現在XML文檔中,多數瀏覽器會把CDATA區域錯誤解析爲Comment或Element。在XML中,可以使用document.createCDataSection()來創建CDATA區域,只需爲其傳入節點的內容。


5.3 DocumentType類型

DocumentType類型在Web瀏覽器中不常用,僅有Firefox、Safari和Opera支持。DocumentType包含着與文檔的doctype有關的所有信息,它具有以下特徵:

1) nodeType的值爲10。

2) nodeName的值爲doctype的名稱。

3) nodeValue的值爲null。

4) parentNode是Document。

5) 沒有子節點。


5.4 DocumentFragment類型

在所有節點類型中,只有DocumentFragment在文檔中沒有對應的標記。DOM規定文檔片段是一種“輕量級”文檔,可以包含和控制節點,但不會佔用額外資源。DocumentFragment節點具有以下特徵:

1) nodeType的值爲11。

2) nodeName的值爲"#document-fragment"。

3) nodeValue的值爲null。

4) praentNode的值爲null。

5) 子節點可以是Element、ProcessingInstruction、Comment、Text、CDATASection或EntiryReference。


5.5 Attr類型

元素的屬性在DOM中以Attr類型來表示。屬性節點具有以下特徵:

1) nodeType的值爲11。

2) nodeName的值是屬性的名稱。

3) nodeValue的值是屬性的值。

4) parentNode的值爲null。

5) 在HTML中沒有子節點。

6) 在XML中子節點可以是Text或EntityReference。

Attr對象有3個屬性:name、value和specified。其中,name是屬性名稱,value是屬性的值,specified是一個布爾值,用以區別屬性是在代碼中指定的,還是默認的。



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