DOM(文檔對象模型)是針對HTML和XML文檔的一個API(應(yīng)用程序編程接口)。DOM描繪了一個層次化的節(jié)點樹,允許添加、移除和修改頁面的一部分。DOM1級為基本的文檔結(jié)構(gòu)即查詢提供了接口。
節(jié)點層次
DOM可以將任何HTML或XML文檔描繪成一個由多層節(jié)點構(gòu)成的結(jié)構(gòu)。節(jié)點分為幾種不同的類型,每種類型分別表示文檔中不同的信息及(或)標記。每個節(jié)點都擁有各自的特定、數(shù)據(jù)和方法,另外也與其它節(jié)點存在某種關(guān)系。節(jié)點之間的關(guān)系構(gòu)成了層次,而所有頁面標記則表現(xiàn)為一個以特定節(jié)點為根節(jié)點的樹形結(jié)構(gòu)。HTML為例:
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<p>Hello World!</p>
</body>
</html>>

文檔節(jié)點是每個文檔的根節(jié)點。在這個例子中,文檔節(jié)點只有一個子節(jié)點,即<html>元素,我們稱之為文檔元素。文檔元素是文檔的最外層元素,文檔中的其他所有元素都包含在文檔元素中。每個文檔只能有一個文檔元素。在HTML頁面中,文檔元素始終都是<html>。在XML中,沒有預(yù)定義的元素,因此任何元素都可能成為文檔元素。
每一段標記都可以通過樹中的一個節(jié)點來表示:HTML元素通過元素節(jié)點表示,特性通過特性節(jié)點表示,文檔類型通過文檔類型節(jié)點表示,而注釋通過注釋節(jié)點表示??偣灿?2種節(jié)點類型,這些類型都繼承自一個基類型。
1.Node類型
DOM1級定義了一個Node接口,該接口將由DOM中的所有節(jié)點類型實現(xiàn)。這個Node接口在JavaScript中是作為Node類型實現(xiàn)的。除了IE之外,其他所有瀏覽器都可以訪問這個類型。JavaScript中的所有節(jié)點都繼承自Node類型,因此所有節(jié)點類型都共享者相同的基本屬性和方法。
每個節(jié)點都有一個nodeType屬性,用于表明節(jié)點的類型。節(jié)點類型由在Node類型中定義的下列12個數(shù)值來表示:
- Node.ELEMENT_NODE(1)
- Node.ATTRIBUTE_NODE(2)
- Node.TEXT_NODE(3)
- Node.CDATA_SECTION_NODE(4)
- Node.ENTITY_REFERENCE_NODE(5)
- Node.ENTITY_NODE(6)
- Node.PROCESSING_INSTRUCTION_NODE(7)
- Node.COMMENT_NODE(8)
- Node.DOCUMENT_NODE(9)
- Node.DOCUMENT_TYPE_NODE(10)
- Node.DOCUMENT_FRAGMENT_NODE(11)
- Node.NOTATION_NODE(12)
通過比較上面這些常量,可以很容易地確定節(jié)點的類型。
if (someNode.nodeType == Node.ELEMENT_NODE) {//在IE中無效
alert("Node is an element.");
}
為了確保瀏覽器兼容,最后還是將nodeType屬性與數(shù)字值進行比較。
if (someNode.nodeType == 1) {//適用于所有瀏覽器
alert("Node is an element.");
}
并不是所有節(jié)點都受到Web瀏覽器的支持,最常用的是元素和文本節(jié)點。
- 1.nodeName和nodeValue屬性
要了解節(jié)點的具體信息,可以使用nodeName和nodeValue這兩個屬性。這兩個屬性的值完全取決于節(jié)點的類型,在使用這兩個值以前,最好先檢測以下節(jié)點的類型。
if (someNode.nodeType == 1) {//適用于所有瀏覽器
value = someNode.nodeName; //nodeName的值是元素的標簽名
}
對于元素節(jié)點,nodeName中保存的始終都是元素的標簽名,而nodeValue的值則始終為null。
- 2.節(jié)點關(guān)系
每一個節(jié)點都有一個childNodes屬性,其中保存著一個NodeList對象。NodeList是一種類數(shù)組對象,用于保存一組有序的節(jié)點,可以通過位置來訪問這些節(jié)點。雖然可以通過方括號語法來訪問NodeList的值,而且這個對象也有l(wèi)ength屬性,但它不是Array的實例。NodeList對象的獨特之處在于,它實際上是基于DOM結(jié)構(gòu)動態(tài)執(zhí)行查詢的結(jié)構(gòu),因此DOM結(jié)構(gòu)的變化能夠自動反映在NodeList對象中。
訪問存在NodeList中的節(jié)點——可以通過方括號,也可以使用item()方法。
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;
length屬性表示的是訪問NodeList那一刻,其中包含的節(jié)點數(shù)量。
對arguments對象使用Array.prototype.slice()方法可以將其轉(zhuǎn)換為數(shù)組,同樣的方法,也可以將NodeList對象轉(zhuǎn)換為數(shù)組。
//在IE8及之前的版本無效
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
function convertToArray(nodes) {
var array = null;
try{
array = Array.prototype.slice.call(nodes,0); //針對非IE瀏覽器
}catch(ex){
array = new Array();
for(var i=0, len = nodes.length; i<len; i++){
array.push(nodes[i]);
}
}
return array;
}
每一個節(jié)點都有一個parentNode屬性,該屬性指向文檔樹中的父節(jié)點。包含在childNodes列表中的每個節(jié)點相互之間都是同胞節(jié)點。通過使用列表中每個節(jié)點的previousSibling和nextSibling屬性,可以訪問同一列表匯總的其他節(jié)點。

hasChildNodes()在節(jié)點包含一或多個子節(jié)點時返回true。
所有節(jié)點都有的最后一個屬性是ownerDocument,該屬性指向表示整個文檔的文檔節(jié)點。
- 3.操作節(jié)點
因為關(guān)系指針都是只讀的,所有DOM提供了一些操作節(jié)點的方法。最常用的方法是appendChild(),用于向childNodes列表的末尾添加一個節(jié)點。添加節(jié)點后,childNodes的新增節(jié)點、父節(jié)點及以前的最后一個子節(jié)點的關(guān)系指針都會相應(yīng)的得到更新。更新完成后,appendChild()返回新增的節(jié)點。
var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true
如果傳入到appendChild()中的節(jié)點已經(jīng)是文檔的一部分,那結(jié)果就是將該節(jié)點從原來的位置轉(zhuǎn)移到新位置上。
var returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); //false
alert(someNode.lastChild ==someNode.lastChild); //true
如果需要把節(jié)點放在childNodes列表中某個特定的位置上,而不是放在末尾,那么可以使用insertBefore()方法。這個方法接受兩個參數(shù):要插入的節(jié)點和作為參照的節(jié)點。插入節(jié)點后,被插入的節(jié)點會變成參照節(jié)點的前一個同胞節(jié)點(previousSibling),同時被方法返回。如果參照節(jié)點是null,則insertBefore()和appendChild()執(zhí)行相同的操作。
returnedNode = someNode.insertBefore(newNode,null);
alert(newNode == someNode.lastChild); //true
returnedNode = someNode.insertBefore(newNode,someNode.firstChild);
alert(returnedNode == newNode); //true
alert(newNode == someNode.firstChild); //true
returnedNode = someNode.insertBefore(newNode,someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true
replaceChild()方法接收兩個參數(shù):要插入的節(jié)點和要替換的節(jié)點。要替換的節(jié)點將有這個方法返回并從文檔樹中被移除,同時由要插入的節(jié)點占據(jù)其位置。
var returnedNode = someNode.replaceChild(newNode,someNode.firstChild);
returnedNode = someNode.replaceChild(newNode,someNode.lastChild);
如果只想移除而非替換節(jié)點,可以使用removeChild()方法。這個方法接受一個參數(shù),即要移除的節(jié)點,被移除的節(jié)點將成為方法的返回值,
var formerFirstChild = someNode.removeChild(someNode.firstChild);
var formerFirstChild = someNode.replaceChild(someNode.lastChild);
- 4.其他方法
有兩個方法是所有類型的節(jié)點都有的。cloneNode()和normalize()。
cloneNode(),用于創(chuàng)建調(diào)用這個方法的節(jié)點的一個完全相同的副本。cloneNode()方法接受一個布爾值參數(shù),表示是否執(zhí)行深復(fù)制。在參數(shù)為true的情況下,執(zhí)行深復(fù)制,也就是復(fù)制節(jié)點及其整個子節(jié)點樹;在參數(shù)為false的情況下,執(zhí)行淺復(fù)制,只復(fù)制 節(jié)點本身。
cloneNode()方法不會復(fù)制添加到DOM節(jié)點中的JavaScript屬性,如事件處理程序等。只復(fù)制特性、子節(jié)點。
normalize(),這個方法唯一的作用就是處理文檔樹中的文本節(jié)點。由于解析器的實現(xiàn)或DOM操作等原因,可能會出現(xiàn)文本節(jié)點不包含文本,或接連儲量倆個文本節(jié)點的情況。當在某個節(jié)點上調(diào)用這個方法時,就會在該節(jié)點的后代節(jié)點中中查找上述兩種情況。如果找到了空文本節(jié)點,刪除它;如果找到相鄰的文本節(jié)點,合并它們。