DOM
(文檔對象模型)是針對HTML和XML文檔的一個API(應用程序編程接口)。DOM描繪了一個層次化的節點樹,容許開發人員添加、移除和修改頁面的某一部分。DOM
脫胎於Netscape及微軟公司創始的DHTML(動態HTML),但如今它已經成爲表現和操做頁面標記的真正跨平臺、語言中立方式。COM
對象的形式實現的。這意味着IE中的DOM
對象與原生JavaScript對象的行爲或活動特色並不一致。本章將較多的談及這些差別。<html> <head> <title>Sample Page</title> </head> <body> <p>Hello World!</p> </body> </html>
<html>
元素,咱們稱之爲文檔元素。<html>
元素。在XML中,沒有預約義的元素,所以任何元素均可能成爲文檔元素。Node
接口,該接口將由 DOM 中全部節點類型實現。這個Node接口在JavaScript中是做爲Node
類型實現的;除了IE以外,在其餘全部瀏覽器中均可以訪問到這個類型。Node
類型,所以全部節點類型都共享着相同的基本屬性和方法。每一個節點都有一個nodeType
屬性,用於代表節點的類型。及誒單類型由在Node
類型中定義的下列12個數值常量來表示,任何節點類型必居其一(編號爲節點類型常量存儲的數值):javascript
Node.ELEMENT_NODE
Node.ATTRIBUTE_NODE
Node.TEXT_NODE
Node.CDATA_SECTION_NODE
Node.ENTITY_REFERENCE_NODE
Node.ENTITY_NODE
Node.PROCESSING_INSTRUCTION_NODE
Node.COMMENT_NODE
Node.DOCUMENT_NODE
Node.DOCUMENT_TYPE_NODE
Node.DOCUMENT_FRAGMENT_NODE
Node.NOTATION_NODE
// 經過比較上面的常量,很容易的肯定節點類型 // 在IE中無效 if (someNode.nodeType == Node.ELEMENT_NODE) { console.log("Node is an element"); } // 因爲IE沒有公開 Node 類型的構造函數 // 最好仍是將 nodeType 屬性與數字比較 if (someNode.nodeType == 1) { console.log("Node is an element"); }
nodeName
和 nodeValue
屬性nodeName
和 nodeValue
兩個屬性。這兩個屬性的值徹底取決於節點類型。在使用這兩個值之前,最好用上述的代碼檢查節點的類型。if (someNode.nodeType == 1) { value = someNode.nodeName; // nodeName的值是元素的標籤名 }
childNodes
屬性,其中保存着一個NodeList
對象。注意,能夠經過方括號語法來訪問NodeList
的值,並且也有length
屬性,但它並非Array
的實例。NodeList
對象的獨特之處在於,它其實是基於DOM結構動態執行查詢的結果,所以DOM結構的變化可以自動反映在NodeList
對象中。咱們常說NodeList
是有生命、有呼吸的對象,而不是咱們第一次訪它的瞬間拍攝下來的一張快照。// 方括號和 item() 語法結果是相同的 var firstChild = someNode.childNodes[0]; var secondChild = someNode.childNodes.item(1); var count = comeNode.childNodes.length; // 雖然不是Array的實例,但咱們能夠將它轉換成數組 // 在IE8及以前的版本中無效 var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes, 0);
// 因爲IE8及更早版本將 NodeList 實現爲一個 COM 對象 // 必須手動枚舉全部成員,才能轉換成數組 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; }
parentNode
屬性,指向文檔中的父節點。childNodes
中的全部節點都具備相同的父節點,而相互之間是同胞節點。previousSibling
和 nextSibling
屬性能夠訪問同一列表中的其餘節點。列表第一個節點previousSibling
爲null
,列表最後一個nextSibling
爲null
,固然若是列表只有一個節點,那麼兩個都是null
。firstChild
和lastChild
屬性分別指向第一個和最後一個。若是列表沒有節點,那麼兩個屬性都是null
。hasChildNodes()
也是一個很是有用的方法,當查詢節點存在子節點時返回true
,不存在返回false
。這是比查詢childNodes.length
更簡單的方法。ownerDocument
,該屬性指向表示整個文檔的文檔節點。這種關係表示的是任何節點都屬於它所在的文檔,任何節點都不能同時存在兩個或更多個文檔中。經過這個屬性,咱們能夠沒必要在節點層次中經過層層回溯達到頂端,而是能夠直接訪問文檔節點。appendChild()
,用於向childNodes
列表的末尾添加一個節點,執行後,方法返回新增的節點。var returnedNode = someNode.appendChild(newNode); console.log(returnedNode == newNode); // true console.log(someNode.lastChild ==newNode); // true
childNodes
列表中某個特定的位置上,而不是放在末尾,可使用insertBefore()
方法。這個方法接收兩個參數:要插入的節點和做爲參照的節點。插入節點後,被插入的節點會變成參照節點的前一個同胞節點(previousSibling),同時被方法返回。若是參照節點是null
,則 insertBefore()
和 appendChild()
執行相同操做。// 插入後成爲最後一個子節點 var returnedNode = someNode.insertBefore(newNode, null); // 插入後成爲第一個子節點 var returnedNode = someNode.insertBefore(newNode, someNode.firstChild); // 插入後在最後一個子節點前面 var returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
replaceChild()
替換節點。一樣接收兩個參數,插入的節點和參照節點。插入新的節點並將參照節點從文檔樹中移除,新的節點會從被替換的節點複製全部關係指針。儘管從技術上講,被替換的節點仍然在文檔中,但它在文檔中的位置已經不存在了。removeChild()
移除節點。被移除的節點仍然在文檔中,但它在文檔中的位置已經不存在了。cloneNode()
用於建立調用這個方法的節點的一個徹底相同的副本。接收一個布爾值參數,表示是否執行深複製。css
appendChild()
insertBefore()
replaceChild()
將它添加到文檔中。clone()
方法不會複製添加到DOM節點中的JavaScript屬性,例如時間處理程序。這個方法只複製特性、(在明確指定的狀況下也複製)子節點,其餘一切都不會複製。<ul id="ul"> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul>
var myList = document.getElementById("ul"); var deepList = myList.cloneNode(true); // [text, li, text, li, text, li, text] console.log(deepList.childNodes); // 3 (IE < 9) 或 7 (其餘瀏覽器) // IE8及以前的版本不會爲包含空白符的文字建立節點(TEXT) console.log(deepList.childNodes.length); var shallowList = myList.cloneNode(false); console.log(shallowList.childNodes.length); // 0
normalize()
方法惟一的做用就是處理文檔樹中的文本節點。因爲解析器的實現或DOM操做等緣由,可能會出現文本節點不包含文本,或者鏈接出現兩個節點的狀況。當在某個節點上調用這個方法時,就會在該節點的後代節點中查找上述兩種狀況。html
var html = document.documentElement; // 取得對<html>的引用 console.log(html == document.childNodes[0]); // true console.log(html == document.firstchild) // true
document.documentElement
和 document.boyd
屬性<!DOCTYPE>
標籤當作一個與文檔其餘部分不一樣的實體,能夠經過doctype屬性(在瀏覽器中是document.doctype
)來訪問信息。瀏覽器對document.doctype
的支持差異很大,因此這個屬性的用途頗有限:java
document.doctype
的值始終爲null
document.doctype
是一個DocumentType節點,也能夠經過document.firstChild
或document.childNodes[0]
訪問同一個節點。document.doctype
是一個DocumentType節點,但該節點不會出如今document.childNodes
中。<html>
元素外部的註釋應該是算是文檔的子節點。然而,不一樣的瀏覽器在是否解析這些註釋以及可否正確處理他們等方面,也存在很大差別。<!-- 第一條註釋 --> <html> <body> </body> </html> <!-- 第二條註釋 -->
看起來這個頁面應該有3個子節點:註釋、<html>
元素、註釋。從邏輯上講,咱們會認爲document.childNodes
中應該包含與這3個節點對應的3項。可是實際上,瀏覽器存在如下差別:node
document.childNodes
中的第一個子節點。document
對象上調用appendChild()
removeChild()
replaceChild()
方法,由於文檔類型(若是存在的話)是隻讀的,並且它只能有一個元素子節點(該節點一般早就已經存在了)。document
對象還有一些標準的Document對象所沒有的屬性。title
包含着<title>
元素中的文本。經過這個屬性能夠取得當前頁面的標題,也能夠修改當前頁面的標題並反映在瀏覽器的標題欄中。修改title
屬性的值會改變<title>
元素。// 取得文檔標題 var originalTitle = document.title; // 設置文檔標題 document.title = "New page title";
下面三個屬性與網頁的請求有關,全部這些信息都存在於請求的HTTP頭部,只不過是經過這些屬性讓咱們可以在JavaScript中訪問它們而已:編程
URL
屬性中包含頁面完整的URL(地址欄中的URL)domain
屬性中值包含頁面的域名referrer
屬性中可能會包含空字符串URL
與domain
屬性是相互關聯的。例如document.URL
等於"http://www.wrox.com/WileyCDA/",那麼document.domain
就等於"www.wrox.com"。domain
能夠設置,但有安全方面的限制。若是URL中包含一個子域名,例如"p2p.wrox.com",那麼就只能講domain
設置爲"wrox.com"(URL中包含"www",如"www.wrox.com"時,也是如此)。document.domain
就很是方便了。因爲跨域安全限制,來自不一樣子域的頁面沒法經過JavaScript通訊。而經過將每一個頁面的document.domain
設置爲相同的值,這些頁面就能夠互相訪問對方包含的JavaScript對象了。// 取得完整的URL var url = document.URL; // 取得域名 var domain = document.domain; // 取得來源 頁面的URL var referrer = document.referrer;
domain
屬性還有一個限制,即若是域名一開始是「鬆散的」(loose),那麼就不能將它再設置爲「緊繃的」(tight)。// 假設頁面來自於 p2p.wrox.com域 document.domain = "wrox.com"; // 鬆散的(成功) document.domain = "p2p.wrox.com"; // 緊繃的(出錯)
getElementById()
接收一個參數:要取得的元素的ID。找到相應的元素則返回該元素,不然返回null
。跨域
<input type="text" name="myElement" value="Text field"> <div id="myElement">A div</div> <script> // IE7中調用會返回<input>元素 var el = document.getElementById("myElement"); </script>
getElementsByTagName()
接收一個參數:要取得的元素的標籤名,而返回的是包含零或多個元素的NodeList
。可使用方括號語法或item()
方法來訪問對象中的項。namedItem()
使用這個方法能夠經過元素的name特性取得集合中的項。或方括號語法能達到一樣的效果<img src="myimage.gif" name="myImage"> <script> var images = document.getElementsByTagName("img"); console.log(images.length); console.log(images[0].src); // 方括號傳入數值就調用 item() console.log(images.item(0).scr); var myImage = images.namedItem("myImage"); var myImage = images["myImage"]; // 方括號傳入字符串就調用namedItem() </script>
getElementsByTagName()
中傳入"*"。在JavaScript及CSS中,星號一般表示所有。getElementsByTagName()
的標籤名是不須要區分大小寫的。但對於XML頁面而言(包括XHTML),getElementsByTagName()
方法就會區分大小寫。getElementByName()
是隻有HTMLDocument類型纔有的方法,返回帶有給定name屬性的全部元素。最常使用的狀況是取得單選按鈕;爲了確保發送給瀏覽器的值正確無誤,全部單選按鈕必須具備相同的name特性<fieldset> <legend>Which color do you prefer?</legend> <ul> <li> <input type="radio" value="red" name="color" id="colorRed"> <label for="colorRed">Red</label> </li> <li> <input type="radio" value="green" name="color" id="colorGreen"> <label for="colorGreen">Green</label> </li> <li> <input type="radio" value="blue" name="color" id="colorBlue"> <label for="colorBlue">Blue</label> </li> </ul> </fieldset>
getElementsByName()
方法能夠返回三個input元素。可是對於這裏的單選按鈕來講namedItem()
方法只會取得第一項(由於每一項的name特性都相同)。document.anchors
包含文檔中全部帶name特性的<a>
元素document.applets
包含文檔中全部的<form>
元素,與document.getElementsByTagName("form")
獲得的結果相同document.images
包含文檔中全部的<img>
元素,與document.getElementsByTagName("img")
獲得的結果相同document.links
包含文檔中全部帶 href
特性的<a>
元素document.implementation
屬性就是爲此提供的,與瀏覽器對DOM的實現直接對應。document.implementation
規定了一個方法,即hasFeature()
。接收兩個參數:要檢測的DOM功能的名稱及版本號。若是支持返回truevar hasXmlDom = docuemnt.implementation.hasFeature("XML", "1.0");
功能 | 版本號 | 說明 |
---|---|---|
Core | 1.0、2.0、3.0 | 基本的DOM,用於描述表現文檔的節點樹 |
XML | 1.0、2.0、3.0 | Core的XML拓展,添加了對CDATA、處理指令及實體的支持 |
HTML | 1.0、2.0 | XML的HTML拓展,添加了對HTML特有元素及實體的支持 |
Views | 2.0 | 基於某些樣式完成文檔的格式化 |
StyleSheets | 2.0 | 將樣式表關聯到文檔 |
CSS | 2.0 | 對層疊樣式表1級的支持 |
CSS2 | 2.0 | 對層疊樣式表2級的支持 |
Events | 2.0, 3.0 | 常規的DOM事件 |
UIEvents | 2.0, 3.0 | 用戶界面事件 |
MouseEvents | 2.0, 3.0 | 由鼠標引起的事件(click、mouseover等) |
MutationEvents | 2.0, 3.0 | DOM樹變化時引起的事件 |
HTMLEvents | 2.0 | HTML4.01事件 |
Range | 2.0 | 用於操做DOM樹種某個範圍的對象和方法 |
Traversal | 2.0 | 遍歷DOM樹的方法 |
LS | 3.0 | 文件與DOM樹之間的同步加載和保存 |
LS-Asnyc | 3.0 | 文件與DOM樹之間的異步加載和保存 |
Validation | 3.0 | 在確保有效的前提下修改DOM樹的方法 |
hasFeature()
方法確實方便,但也有缺點。由於實現者能夠自行決定是否與DOM規範的不一樣部分保持一致。事實上,想讓hasFearture()
針對全部值都有返回true很容易,但返回true有時候也不意味着實現與規範一致。hasFreatrue()
以外,還同時使用能力檢測。write()
和writeln()
方法都接收一個字符串參數,即要寫入到輸出流中的文本。wirte()
會原樣寫入,而writeln()
則會在字符串的末尾添加一個換行符(n)。在頁面加載的過程當中,可使用這兩個方法動態的加入內容。open()
和close()
分別用於打開和關閉網頁的輸出流。若是是在頁面加載期間使用write()
或writeln()
方法,則不須要用到這兩個方法。application/xml+xhtml
內容類型提供的頁面,這兩個方法也一樣無效。Element
類型用於表現XML或XHTML元素,提供了對元素標籤名、子節點及特性的訪問。Element
類型具備如下特徵:數組
nodeType
的值爲1nodeName
的值爲元素的標籤名nodeValue
的值爲null
parentNode
的值可能爲Dcoment或ElementElement
、 Text
、 Comment
、 ProcessingInstruction
、 CDATASection
、 EntityReference
nodeName
屬性,也能夠是使用tagName
屬性,這兩個屬性會返回相同的值。var div = document.getElementById("myDiv"); console.log(div.tagName); // "DIV" console.log(div.nodeName); // "DIV" console.log(div.tagName == div.nodeName); // true
if (element.tagName == "div") { // 不能這樣比較,很容易出錯 } if (element.tagName.toLowerCase() == "div") { // 推薦這樣作(適用於任何文檔) }
全部HTML元素都由HTMLElement
類型表示。HTMLElement
類型直接繼承自Elment並添加了一些屬性。每一個HTML元素中都存在的下列標準特性:瀏覽器
id
元素在文檔中的惟一標識符title
有關元素的附加說明信息,通常經過工具提示條顯示出來lang
元素內容的語言代碼,不多使用dir
語言的方向值爲"ltr"(left-to-right 從左至右)或 "rtl"className
與元素的class特性對應,即爲元素指定的CSS類。沒有將這個屬性命名爲class是由於class是ECMAScript的保留字。getAttribute()
、setAttribute()
、 removeAttribute()
。var div = document.getElemntByid("myDiv"); console.log(div.getAttribute("id")); // "myDiv" console.log(div.getAttribute("class")); // "bd" console.log(div.getAttribute("title")); // "Body Text" console.log(div.getAttribute("lang")); // "en" console.log(div.getAttribute("dir")); // "ltr"
getAttribute()
的特性名與實際的特性名相同。所以想要獲得class特性值,應該傳入"class" 而不是"className",後者只在經過對象屬性訪問特性時才用。getAttribute()
返回null
。HTMLElement
也會有5個屬性與相應的特性一一對應。不過只有公認的(非自定義)特性纔會以屬性的形式添加到DOM對象中。例如能夠經過div.id
訪問div元素的id屬性。不過自定義特性在Safari、Opera、Chrome、Firefox中是不存在的,但IE卻會爲自定義特性也建立屬性。getAttribute()
訪問時,返回的style特性值中包含的是CSS文本,而經過屬性來訪問它則會返回一個對象。因爲style屬性是用於以編程方式訪問元素樣式的(本章後面討論),所以並無直接映射到style特性。getAttribute()
訪問,返回的是相應的代碼字符串。而在訪問onclick屬性時,則返回的是一個JavaScript函數(若是未在元素中指定相應特性,則返回null
)。這是由於onclick及其餘事件程序屬性自己就應該被賦予函數值。getAttribute()
方法,而只是使用對象的屬性。只有在取得自定義特性值得狀況下,纔會使用getAttribute()
方法。getAttribute()
訪問style特性或onclick,返回的值與屬性相同,都返回對象值或函數值。雖然IE8已經修復了這個bug,但不一樣IE版本間的不一致性,也是致使開發人員不適用getAttribute()
訪問HTML特性的一個緣由。getAttribute()
對應的方法時setAttribute()
這個方法接收兩個參數:要設置的特性名和值。若是特性已經存在,setAttribute()
會以指定的值替換現有的值;若是特性不存在,則建立該屬性並設置相應的值。setAttribute()
方法既能夠操做HTML特性也能夠操做自定義特性。經過這個方法設置的特性名會統一轉換爲小寫形式,即"ID"最終變成"id"。div.setAttribute("id", "someOtherId"); div.id = "someOtherId"; // 添加自定義屬性,該屬性不會自動成爲元素的特性 div.mycolor = "red"; div.getAttribute("mycolor"); // null ie除外
removeAttribute()
用於完全刪除元素的特性,調用這個方法不只會清楚特性的值,並且也會從元素中徹底刪除特性。這個方法並不經常使用,IE6及之前版本不支持。div.removeAttribute("class");
NamedNodeMap
,與NodeList
相似,也是一個動態集合。元素的每個 特性都由一個Attr
節點表示,每一個節點都保存在NamedNodeMap
對象中。NamedNodeMap
對象擁有如下方法緩存
getNamedItem(name)
:返回nodeName
屬性等於name
的節點removeNamedItem(name)
:從列表中移除nodeName
屬性 等於name的節點setNameItem(node)
:向列表中添加節點,以節點的nodeName
屬性爲索引item(pos)
:返回位於數字pos位置處的節點attributes
屬性中包含一系列節點,每一個節點的nodeName
就是特性的名稱,而節點的nodeValue
就是特性的值。// 取得元素的id var id = element.attributes.getNamedItem("id").nodeValue; // 設置元素的id element.attributes["id"].nodeValue = "someOtherId"; // 刪除元素id,並返回被刪除特性的Attr節點 var oldAttr = element.attributes.removeNamedItem("id"); // 傳入一個新的特性節點 element.attributes.setNameItem(newAttr);
getAttribute()
、removeAttribute()
、setAttribute()
方法。若是想要遍歷元素特性,能夠用attributes// 迭代元素的每個特性,而後構形成 name="value"字符串 function outputAttributes(element) { var pairs = new Array(), attrName, attrValue, i, len; for (i=0, len=elment.attributes.length; i < len; i++) { attrName = element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; // 針對 IE7- 作兼容 // 根據specified屬性,只返回指定的特性 if (element.attributes[i].specified) { paris.push(attrName + "=\"" + attrValue + "\""); } } return pairs.join(" "); }
document.createElement()
方法能夠建立新元素。只接收一個參數,即要建立元素的標籤名,在HTML文檔中不區分大小寫,而在XML(包括XHTML)文檔中,則是區分大小寫。document.createElement()
建立元素的同時,也爲新元素設置了ownerDcoument
屬性。此時還能夠操做元素的特性,爲它添加更多子節點。appendChild()
insertBefore()
replaceChild()
方法。// 建立 var div = document.createElement("div"); // 操做元素特性,添加子節點 div.id = "myNewDiv"; div.className = "box"; document.body.appendChild(div);
在IE中能夠傳入完整的元素標籤,也能夠包含屬性(僅IE支持)。這樣有助於避開在IE7及更早版本中動態建立元素的某些問題:
<iframe>
元素的name
特性reset()
方法重設動態建立的<input>
元素(第13章討論reset()方法)type
特性值爲「reset」的<button>
元素重設不了表單name
相同的單選按鈕彼此毫無關係。if (client.browser.id && client.browser.ie <= 7) { var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div>"); }
childNodes
屬性中包含了它全部子節點,這些子節點多是元素、文本節點、註釋或處理指令。不用瀏覽器在看待這些節點方面存在顯著的不一樣。<ul id="myList"> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul>
<ul>
元素會有3個子節點,分別是3個<li>
元素。但若是是其餘瀏覽器,<ul>
元素都會有7個元素,包括3個<li>
元素和4個文本節點(表示<li>
元素之間的空白符)。<ul id="myList"><li>item 1</li><li>item 2</li><li>item 3</li></ul>
childNodes
屬性遍歷子節點,那麼必定不要忘記瀏覽器間的這一差異。這意味着在執行某項操做之前,一般都要先檢查nodeType
屬性for (var i=0, len = element.childNodes.length; i < len; i++) { if (element.childNodes[i].nodeTpe == 1) { ... } }
getElementsByTagName()
方法,結果只會返回當前元素的後代。var ul = document.getElementById("myList"); var items = ul.getElementsByTagName("li");
Text節點具備如下特徵:
nodeType
的值爲3nodeName
的值爲'#text'nodeValue
的值爲節點所包含的文本parentNode
是一個ElementnodeValue
屬性或data
屬性訪問Text節點中包含的文本,這兩個屬性的值相同。對nodeValue
的修改也會經過data
反映出來,反之亦然。使用下列方法能夠操做節點中的文本
appendData(text)
:將text添加到節點的末尾deleteData(offset, count)
:從offset指定的位置插入textinsertData(offset, text)
:在offset指定的位置插入textreplaceData(offset, count, text)
:用text替換從offset指定的位置開始到 offset+count爲止處的文本splitText(offset)
:從offset指定的位置將當前文本節點分紅兩個文本節點。substringData(offset, count)
:提取從offset指定的位置開始到 offset+count爲止處的字符串length
屬性:保存着節點中字符的書目。並且nodeValue.length
和data.length
中也保存着一樣的數值<!-- 沒有內容,也就沒有文本節點 --> <div></div> <!-- 有空格,由於有一個文本節點 --> <div> </div> <!-- 有內容,由於有一個文本節點 --> <div>Hello World!</div>
// 能夠像這樣取得文本子節點 var textNode= div.firstChild; // 或者 div.childNodes[0] // 取得文本節點的引用後,就能夠修改它了 div.firstChild.nodeValue = "Some other message";
div.firstChild.nodeValue = "Some <strong>other</strong> message"; // 輸出結果:"Some <strong>other</strong> message"
document.createTextNode()
建立新的文本節點。與設置已有文本節點的值同樣,做爲參數的文本也將按照HTML或XML的格式進行編碼。var textNode = document.createTextNode("<strong>Hello</strong> World!");
ownerDocument
屬性。不過除非把新節點添加到文檔樹中已經存在的節點中,不然咱們不會在瀏覽器窗口中看到新節點。var element = document.createElement("div"); elment.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element);
var element = document.createElement("div"); elment.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); var anotherTextNode = document.createTextNode("Yippee!"); element.appendChild(anotherTextNode); document.body.appendChild(element);
normalize()
方法是由Node
類型定義的(於是在全部節點類型中都存在)。若是在一個包含多個文本節點的父元素上調用normalize()
方法,則會將全部文本節點合併成一個文本節點。var element = document.createElement("div"); elment.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); var anotherTextNode = document.createTextNode("Yippee!"); element.appendChild(anotherTextNode); document.body.appendChild(element); console.log(element.childNodes.length); // 2 element.normalize(); console.log(element.childNodes.length); // 1 console.log(element.firstChild.nodeValue); // "Hello World!Yippee!"
normalize()
有時候會致使IE6崩潰,IE7以上修復了此問題。splitText()
方法會將一個文本節點分割成兩個。var element = document.createElement("div"); elment.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element); var newNode = element.firstChild.splitText(5); console.log(element.firstChild.nodeValue); // "Hello" console.log(newNode.nodeValue); // " World!" console.log(element.childNodes.length); // 2
註釋在DOM中是經過Comment類型來表示的。Comment
節點具備如下特徵:
nodeType
的值爲8nodeName
的值爲 "#comment"nodeValue
的值是註釋的內容parentNode
多是Dcoment或ElementsplitText()
以外的全部字符串操做方法。<div id="myDiv"><!--A comment--></div>
var div = document.getElementById("myDiv"); var comment = div.firstChild; console.log(comment.data); // "A comment"
document.createComment()
併爲其傳遞註釋文本也能夠建立註釋節點var comment = document.createComment("A comment ");
</html>
標籤後的註釋。若是要訪問註釋節點,必定要保證它們是位於<html>
和</html>
之間。splitText()
以外的全部字符串操做方法。CDATASection節點具備下列特徵:
nodeType
的值爲4nodeName
的值爲"#cdata-section"nodeValue
的值是CDATA區域中的內容parentNode
多是Document
或Element
<div id="myDiv"><![CDATA[This is some content.]]></div>
document.createCDataSection()
來建立CDATA區域。DocumentType類型在Web瀏覽器中並不經常使用,僅有 Firefox Safari 和 Opera支持它。
nodeType
的值爲10nodeName
的值爲doctype的名稱nodeValue
的值是null
parentNode
是Document
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
console.log(document.doctype.name); // "HTML"
document.doctype
的值始終都是null
DOM規定文檔片斷(document fragment)是一種輕量級的文檔,能夠包含和控制節點,但不會像完整的文檔那樣佔用額外的資源。
nodeType
的值爲11nodeName
的值爲"#document-fragment"nodeValue
的值是null
parentNode
是null
Element
、ProcessingInstruction
、Comment
、Text
、 CDATASection
、EntityReference
document.createDocumentFragment()
方法建立文檔片斷<ul id="myList"></ul>
var fragment = document.createDocumentFragment(); var ul = document.getElementById("myList"); var li = null; // 若是直接向ul添加li元素會致使瀏覽器反覆渲染 // fragment做爲一個元素中轉的倉庫避免了這個問題 for (var i=0; i < 3; i++) { li = document.createElement("li"); li.appendChild(document.createTextNode("Item " + (i+1))); fragment.appendChild(li); } // 這裏只會將fragment的全部子節點添加到ul上 // 而fragment自己永遠不會成爲文檔樹的一部分 ul.appendChild(fragment);
元素的特性在DOM中以Attr類型來表示。在全部瀏覽器中(包括IE8),均可以訪問 Attr類型的構造函數和原型。
nodeType
的值爲2nodeName
的值就是特性的名稱nodeValue
的值就是特性的值parentNode
是null
name
value
specified
。document.createAttribute()
傳入特性的名稱能夠建立新的特性節點。var attr = document.createAttribute("align"); attr.value = "left"; element.setAttribute(attr); console.log(element.attributes["align"].value); // left console.log(element.getAttributeNode("align").value); // left console.log(element.getAttribute("align")); // left
<script>
元素能夠向頁面中插入JavaScript代碼,一種是經過其src特性包含外部文件,另外一種就是用這個元素自己包含代碼。// 在執行最後一行代碼把<script>元素添加到頁面中以前 // 是不會下載外部文件的 var script = document.createElement("script"); script.type = "text/javascript"; script.src = "client.js"; document.body.appendChild(script);
<script type="text/javascript" src="client.js"></script>
<script>
視爲一個特殊元素,不容許DOM訪問其子節點。不過可使用<script>
元素的text屬性來制定JavaScript代碼var script = document.createElement("script"); script.type = "text/javascript"; // 這樣IE不支持 script.appendChild( document.createTextNode("function sayHi() { console.log('Hi')}") ); // 可使用`<script>`元素的text屬性來制定JavaScript代碼 script.text = "function sayHi() { console.log('Hi')}"; document.body.appendChild(script);
var script = document.createElement("script"); script.type = "text/javascript"; var code = "function sayHi() { console.log('Hi')}" try { script.appendChild(document.createTextNode(code)); } catch (ex) { script.text = code; } document.body.appendChild(script);
eval()
是同樣的。var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = "style.css"; var head = document.getElementsByTagName("head")[0]; head.appendChild(head);
<link rel="stylesheet" type="text/css" href="styles.css">
<link>
元素添加到<head>
而不是<body>
元素,才能保證所在瀏覽器中的行爲一致。function loadStyleString(css) { var style = document.createElement("style"); style.type = "text/css"; try { style.appendChild(document.createTextNode(css)); } catch (ex) { style.styleSheet.cssText = css; } document.getElementsByTagName("head")[0].appendChild(style); }
styleSheet.cssText
屬性。在重用同一個<style>
元素並再次設置這個屬性時,有可能致使瀏覽器崩潰。一樣將cssText
屬性設置爲空字符串也可能致使瀏覽器崩潰。<table>
元素是HTML中最複雜的結構之一。想要建立表格,通常都必須涉及表示表格行、單元格、表頭等方面。因爲涉及的標籤多,於是使用核心DOM方法建立和修改表格每每都免不了要編寫大量的代碼。<table border="1" width="100%"> <tbody> <tr> <td>Cell 1,1</td> <td>Cell 2,1</td> </tr> <tr> <td>Cell 1,2</td> <td>Cell 2,2</td> </tr> </tbody> </table>
// 使用核心DOM方法建立這些元素 // 建立table var table = document.createElement("table"); table.border = 1; table.width = "100%"; // 建立tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); // 建立第一行 var row1 = document.createElement("tr"); tbody.appendChild(row1); var cell1_1 = document.createElement("td"); cell1_1.appendChild(document.createTextNode("Cell 1,1")); row1.appendChild(cell1_1); var cell2_1 = document.createElement("td"); cell2_1.appendChild(document.createTextNode("Cell 2,1")); row1.appendChild(cell2_1); // 建立第二行 var row2 = document.createElement("tr"); tbody.appendChild(row2); var cell1_2 = document.createElement("td"); cell1_2.appendChild(document.createTextNode("Cell 1,2")); row2.appendChild(cell1_2); var cell2_2 = document.createElement("td"); cell2_2.appendChild(document.createTextNode("Cell 2,2")); row2.appendChild(cell2_2); // 將表格添加到文檔主體中 document.body.appendChild(table);
<table>
<tbody>
<tr>
元素添加了一些屬性和方法。<table>
元素添加的屬性和方法:
<caption>
元素(若是有)的指針<tbody>
元素的HTMLCollction<tfoot>
元素的(若是有)指針<thead>
元素的(若是有)指針<thead>
元素,將其放到表格中,返回引用<tfoot>
元素,將其放到表格中,返回引用<caption>
元素,將其放到表格中,返回引用<thead>
元素<tfoot>
元素<caption>
元素rows
集合中的指定位置插入一行爲<tbody>
元素添加的屬性和方法以下:
<tbody>
元素中行的HTMLCollectionrows
集合中的指定位置插入一行爲<tr>
元素添加的屬性和方法以下:
<tr>
元素中單元格的HTMLCollectioncells
集合中的指定位置插入一個單元格,返回對新插入單元格的引用。// 根據以上屬性和方法,能夠大大簡化前述代碼 // 建立table var table = document.createElement("table"); table.border = 1; table.width = "100%"; // 建立tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); // 建立第一行 tbody.insertRow(0); tbody.rows[0].insertCell(0); tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1")); tbody.rows[0].insertCell(1); tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1")); // 建立第二行 tbody.insertRow(0); tbody.rows[1].insertCell(0); tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2")); tbody.rows[1].insertCell(1); tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2")); // 將表格添加到文檔主體中 document.body.appendChild(table);
NodeList
及其近親 NamedNodeMap
和 HTMLCollection
,是從總體上透徹理解DOM的關鍵所在。這三個集合都是動態的,每當文檔結構發生變化,它們都會獲得更新。NodeList
對象都是在訪問DOM文檔實時運行的查詢。// 下列代碼會致使無限循環 var divs = document.getElementsByTagName("div"); var div; // 每次循環都要對條件 i < divs.length 求值 // 但每次循環都添加了一個新的div for (var i=0; i < divs.length; i++) { div = document.createElement("div"); document.body.appendChild(div); }
// 最好使用length屬性初始化第二個變量 var divs = document.getElementsByTagName("div"); var i, len, div; // len保存着第一次循環時div的數量,不會隨着循環增長 for (i=0, len=divs.length; i < len; i++) { div = document.createElement("div"); document.body.appendChild(div); }
NodeList
的次數,由於每次訪問都會運行一次基於文檔的查詢。能夠考慮將從NodeList
中取得的值緩存起來。