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");
功能 | 版本號 | 說明 |
---|---|---|
Core | 1.0、2.0、3.0 | 基本的DOM,用於描述表現文檔的節點樹。 |
XML | 1.0、2.0、3.0 | Core的XML擴展。 |
HTML | 1.0、2.0 | XML的HTML擴展。 |
Views | 2.0 | 基於某些樣式完成文檔的格式化。 |
StyleSheets | 2.0 | 將樣式表關聯到文檔。 |
CSS | 2.0 | 對層疊樣式表1級的支持。 |
CSS2 | 2.0 | 對層疊樣式表2級的支持。 |
Events | 2.0 | 常規的DOM事件。 |
UIEvents | 2.0 | 用戶界面事件。 |
MouseEvents | 2.0 | 由鼠標引發的事件。 |
MutationEvents | 2.0 | DOM樹變化時引發的事件。 |
HTMLEvents | 2.0 | HTML4.01事件。 |
Range | 2.0 | 用於操作DOM樹中某個節圍的對象和方法。 |
Traversal | 2.0 | 遍歷DOM樹的方法。 |
LS | 3.0 | 文件與DOM樹之間的同步加載和保存。 |
LS-Async | 3.0 | 文件與DOM樹之間的異步加載和保存。 |
Validation | 3.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是一個布爾值,用以區別屬性是在代碼中指定的,還是默認的。