js學習筆記:DOM——節點層次

DOM(文檔對象模型)是針對HTML和XML文檔的一個API,描繪瞭一個層次化的節點樹。
DOM可以將任何HTML或XML文檔描繪成一個由多層節點構成的結構。
節點分為幾種不同的類型。

文檔節點是每個文檔的根節點。html元素為文檔元素

文檔元素是文檔的最外層元素 文檔中的其他所有元素都包含在文檔元素中。 每個文檔都隻能有一個文檔元素。

在html頁面中,文檔元素始終都是html元素。

Node類型

DOM1定義瞭一個Node接口,該接口將由DOM中的所有節點類型實現。

這個Node接口在js中是作為Node類型實現的。js中所有節點類型都繼承自Node類型,因此所有節點類型都共享著相同的基本屬性和方法。

nodeType屬性

每一個節點都有一個nodeType屬性,用於表明節點的類型。
節點類型由在Node類型中定義的下列12個數值常量來表示:

Node.ELEMENT_NODE(1) Node.ARRIBUTE_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)

通過比較節點的nodeType與上面這些常量,可以很容易地確定節點的類型。

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

為瞭確保跨瀏覽器兼容,最好還是將nodeType屬性與屬性值進行比較:

if(someNode.nodeType ==  1){
    alert("node is an element");
}

nodeName和nodeValue屬性

這兩個屬性值完全取決於節點的類型,因此在使用這兩個值以前最好先檢測一下節點的類型:

if(someNode.nodeType ==  1){
    value = someNode.nodeName;   //nodeName的值是元素的標簽名
}

在這個例子中,首先檢查節點類型,看它是不是一個元素。
對於一個元素節點,nodeName中保存的始終都是元素的標簽名,nodeValue的值則始終為null。

節點關系

childNodes

每個節點都有一個childNodes屬性,其中保存著一個NodeList對象。

NodeList是一種類數組對象,用於保存一組有序的節點,可以通過位置來訪問這些節點。

雖然可以通過方括號語法來訪問NodeList的值,而且這個對象也有length屬性,但它並不是Array類型的實例。

NodeList對象的獨特之處在於,它實際上是基於DOM結構動態查詢的結果,因此DOM結構的變化能夠自動反映在NodeList中

下面的例子展示瞭如何訪問保存在NodeList中的節點:可以通過方括號,也可以使用item()方法。

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

其中length屬性表示的是訪問NodeList的那一刻,其中包含的節點數量。

之前介紹過,對arguments對象使用Array.prototype.slice()方法可以將其轉換為數組,我們也可以對NodeList對象采取同樣的方法,從而將其轉換為數組:

var ArrayNodes = Array.prototype.slice.call(someNode.childNodes,0);

parentNode 

每個節點都有一個parentNode屬性,該屬性指向文檔樹中的父節點。

包含在childNodes列表中的所有節點都具有相同的父節點,因此他們的parentNode屬性都指向同一個節點。

previousSibling nextSibling

包含在childNodes列表中的每個節點相互之間都是同胞節點,通過使用每個節點的previousSibling和nextSibling屬性,可以訪問到同一列表中的其他節點。

列表中第一個節點的previousSibling屬性為null。 列表中最後一個節點的nextSibling屬性也為null。 如果列表中隻有一個節點,那麼該節點的previousSibling和nextSibling屬性都為null

firstChild lastChild

父節點的firstChild屬性指向其childNodes列表中的第一個節點。值始終等於childNodes[0]

父節點的lastChild屬性指向其childNodes列表中的最後一個節點。值始終等於childNodes[childNodes.length-1]

在隻有一個子節點的情況下,firstChild和lastChild指向同一個節點。

如果沒有子節點,那麼firstChild和lastChild都為null。

這裡寫圖片描述

hasChildNodes()

此方法在節點包含一或多個子節點的情況下返回true。
比查詢childNodes列表的length屬性更簡單。

ownerDocument

所有節點都有這個屬性,該屬性指向標是整個文檔的文檔節點。

這種關系表示的是任何節點都屬於它所在的文檔,任何節點都不能同時存在於兩個或更多個文檔中。

通過這個屬性,我們可以不必在節點層次中通過層層回溯到達頂端,而是可以直接訪問文檔節點。

操作節點

appendChild()

用於向childNodes列表的末尾添加一個節點。 添加節點後,childNodes的新增節點、父節點、以及以前的最後一個子節點的關系指針都會相應地得到更新。 更新完成後,appendChild()返回新增的節點

var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == returnedNode); //true

如果傳入到appendChild中的節點已經是文檔的一部分瞭,那結果就是將該節點從原來的位置轉移到新位置。
即使可以將DOM樹看成是由一系列指針連接起來的,但任何DOM節點也不能同時出現在文檔中的多個位置上。
因此,如果在調用appendChild()時傳入的是父節點的第一個子節點,那麼該節點就會成為父節點的最後一個子節點。

var returnedNode = someNode.appendChild(someNode.firstChild);
alert(someNode.firstChild == returnedNode); //false
alert(someNode.lastChild == returnedNode); //true

insertBefore()

如果需要把節點放在childNodes列表中某個特定的位置上,而不是放在末尾,那麼可以使用insertBefore方法。這個方法接收兩個參數:要插入的節點和作為參照的節點。

插入節點後,被插入的節點會變成參照節點的前一個同胞節點, 同時被方法返回。

如果參照節點是null,則insertBefore()與appendChild()執行相同的操作。

//插入後成為最後一個子節點
returnedNode = someNode.insertBefore(newNode,null);
alert(someNode.lastChild == returnedNode); //true

//插入後成為第一個子節點
returnedNode = someNode.insertBefore(newNode,someNode.firstNode);
alert(newNode == returnedNode); //true
alert(someNode.firstChild == returnedNode); //true

//插入到最後一個子節點前面
returnedNode = someNode.insertBefore(newNode,someNode.lastNode);
alert(returnedNode == someNode.childNodes[someNode.childNodes.length-2]); //true

到此介紹的appendChild()和insertBefore()都隻插入節點,不會移除節點。

replaceChild()

此方法接收兩個參數,要插入的節點和要替換的節點。
要替換的節點將由這個方法返回並從文檔樹中移除,同時由要插入的節點占據其位置。

//替換第一個子節點
var returnedNode = someNode.replaceChild(newNode,someNode.firstChild);

//替換最後一個子節點
var returnedNode = someNode.replaceChild(newNode,someNode.lastChild);

在使用replaceChild插入一個節點時,該節點的所有關系指針都會從被他替換的節點復制過來。
盡管從技術上講,被替換的節點仍然在文檔中,但它在文檔中已經沒有瞭自己的位置。

removeChild()

如果隻想移除節點而不插入節點,可以使用removeChild()方法。這個方法接收一個參數,即要移除的節點。被移除的節點將成為方法的返回值。

//移除第一個子節點
var returnedNode = someNode.removeChild(someNode.firstChild);

//移除最後一個子節點
var returnedNode = someNode.removeChild(someNode.lastChild);

與使用replaceChild()方法一樣,通過removeChild()移除的節點仍然為文檔所有,隻不過在文檔中已經沒有瞭自己的位置。

前面介紹的幾個方法操作的都是某個節點的子節點,也就是說,要使用這幾個方法都要先取得父節點
如果在不支持子節點的節點上調用瞭這些方法,這會導致錯誤發生。

其他方法

有兩個方法是所有類型的節點都有的。

cloneNode()

用於創建調用這個方法的節點的一個完全相同的副本。
此方法接收一個佈爾值參數,表示是否執行深復制:

true:執行深復制,也就是復制節點及其整個子節點樹。 false:執行淺復制,即隻復制節點本身。

復制後返回的節點副本屬於文檔所有,但並沒有為它指定父節點。


  • item 1
  • item 2
  • item 3
//深復制
var deepList = myList.cloneNode(true);
alert(deeplist.childNodes.length); //3

//淺復制
var deepList = myList.cloneNode(false);
alert(deeplist.childNodes.length); //0

cloneNode()方法不會復制添加到DOM節點中的js屬性,例如事件處理程序等。這個方法隻復制特性,子節點(深復制情況下),其他一切都不會復制。

normalize()

這個方法唯一的作用就是處理文檔樹中的文本節點。
由於解析器的實現或DOM操作等問題,可能會出現文本節點不包含文本,或者接連出現兩個文本節點的情況。
當在某個節點上調用這個方法時,就會在該節點的後代節點中查找上述兩種情況:

如果找到瞭空文本節點則刪除 如果找到相鄰的文本節點則將它們合並為一個文本節點。

Document類型

javascript通過document類型表示文檔。在瀏覽器中,document對象是HTMLDocument(繼承自Document對象)的一個實例,表示整個html頁面。而且,document對象是window對象的一個屬性,因此可以將其作為全局對象來訪問。

Document節點具有下列特征:

nodeType值為9 nodeName值為#document nodeValue值為null parentNode值為null ownerDocument值為null 其子節點可能是一個DocumentType(最多一個)、Element(最多一個)、ProcessingInstruction或Comment

Document類型可以表示HTML頁面或者其他基於XML的文檔。不過,最常見的應用還是作為HTMLDocument實例的document對象。通過這個文檔對象,不僅可以取得與頁面有關的信息,而且還能操作頁面的外觀及其底層結構。

文檔的子節點

documentElement

有兩個內置的訪問其子節點的快捷方式:

documentElement屬性:該屬性始終指向HTML頁面中的< html >元素,較為方便快捷 通過childNodes列表訪問文檔元素

以下面的這個頁面為例:


這個頁面在經過瀏覽器解析後,其文檔中隻包含一個子節點,即html元素。可以通過documentElement屬性或childNodes列表訪問這個元素

var html = document.documentElement; ////取得對

的引用 alert(html == document.childNodes[0]); //true alert(html == document.firstChild); //true

這個例子說明,documentElement、childNodes[0]、firstChild都指向< html>元素。

body

作為HTMLDocument的實例,document對象還有一個body屬性,直接指向< body >元素。

var body = document.body; //取得對

的引用

所有瀏覽器都支持documentElement和body屬性。

doctype

Document另一個可能的子節點是DocumentType。
通常將< !DOCTYPE>標簽看成一個與文檔其它部分不同的實體,可以通過doctype屬性訪問。

var doctype = document.doctype; // 取得對的引用

由於瀏覽器對document.doctype的支持不一致,因此這個屬性的用處有限。

多數情況下,我們都用不著在document對象上調用appendChild()、removeChild()和replaceChild()方法,因為文檔類型(如果存在的話)是隻讀的,而且document隻能有一個元素子節點。

文檔信息

作為HTMLDocument的一個實例,document對象還有一些標準的Document對象所沒有的屬性,這些屬性提供瞭document對象所表現的網頁的一些信息。

title

title屬性包含著< title >元素中的文本——顯示在瀏覽器窗口的標題欄或標簽頁上。
通過這個屬性可以取得當前頁面的標題,也可以修改當前頁面的標題並反映在瀏覽器的標題欄中。
修改title屬性的值會改變< title >元素。

//取得文檔標題
var originalTitle = document.title;

//設置文檔標題
document.title = "new page title";

URL domain referrer

接下來介紹的三個屬性都與對網頁的請求有關,他們是URL、domain、referrer。

URL屬性中包含著頁面完整的URL(即地址欄中顯示的URL) domain屬性中隻包含頁面的域名 referrer屬性中則保存著鏈接到當前頁面的那個頁面的URL。在沒有來源頁面的情況下,referrer屬性中可能會包含空字符串。

所有這些信息都存在於請求的HTTP頭部,隻不過是通過這些屬性讓我們能夠在js中訪問它們而已。

//取得完整的URL
var url = document.URL;

//取得域名
var domain = document.domain;

//取得來源頁面的URL
var referrer = document.referrer;

URL與domain屬性是相互關聯的,如document.URL為 https://www.wrox.com/WileyCDA;那麼document.domain就等於 www.wrox.com。

在這3個屬性中,隻有domian是可以設置的。但由於安全方面的限制,也並非可以給domain設置任何值,不能將這個屬性設置為URL中不包含的域。

//假設頁面來自p2p.wrox.com域
document.domian = "wrox.com"; //成功
document.domain = "nzonline.net"; //出錯

由於跨域安全限制,來自不同子域的頁面無法通過js通信。而通過將每個頁面的document.domain設置為相同的值,這些頁面就可以互相訪問對方包含的js對象瞭。

例如:有一個頁面加載自www.wrox.com,其中包含一個內嵌框架,框架內的頁面加載自p2p.wrox.com。由於document.domain字符串不一樣,內外兩個頁面之間無法互相訪問對方的js對象。但如果將這兩個頁面的document.domain都設置為“wrox.com”,它們之間就可以通信瞭。

瀏覽器對domain屬性還有一個限制,如果域名一開始是“松散的”(loose),那麼不能將它再設置為緊繃的(tight)。

//假設頁面來自p2p.wrox.com域
document.domian = "wrox.com"; //松散的,成功
document.domain = "p2p.wrox.com"; //緊繃的,出錯

查找元素

getElementById()

接收一個參數:要取得的元素Id。如果找到相應的元素就返回該元素,如果沒有找到就返回null。
這裡的id必須和頁面中元素的id特性嚴格匹配,包括大小寫。


some text

下面的代碼可以取得對p元素的引用:

var p = document.getElementById("myDiv");

下面的代碼卻會返回null

var p = document.getElementById("myp");

如果頁面中多個元素的ID值相同,getElementById()隻返回文檔中第一次出現的元素。

getElementsByTagName()

這個方法接收一個參數,即要取得元素的標簽名,返回的是包含零或多個元素的NodeList。在HTML文檔中,這個方法會返回一個HTMLCollection對象,作為一個“動態”集合,該對象與NodeList類似。

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

這行代碼會將一個HTMLCollection對象保存在images變量中。與NodeList對象類似,可以使用方括號或item()方法來訪問HTMLCollection對象中的項。這個對象中元素的數量則可以通過其length屬性取得。

images.length;  //輸出圖像的數量
images[0].src;  //輸出第一個圖像元素的src特性
iamges.item(0).src;  //輸出第一個圖像元素的src特性

HTMLCollection對象還有一個方法,叫做namedItem(),使用這個方法可以通過元素的name特性取得集合中的項。


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

在提供按索引訪問項的基礎上,HTMLCollection還支持按名稱訪問項:

var myImage = images["myImage"];

總結來說,對HTMLCollection而言,我們可以向方括號中傳入數值或字符串形式的索引值:

對數值索引就會調用item() 對字符串索引就會調用namedItem()

要取得文檔中的所有元素,可以向getElementsByTagName()中傳入“ * ”。在js及css中,“ *“ 表示全部。

var allElements = document.getElementByTagName("*");

這行代碼返回的HTMLCollection中,就包含瞭整個頁面中的所有元素——按照他們出現的先後順序。

雖然標簽名是區分大小寫的,但為瞭最大限度與既有html頁面兼容,傳給getElementByTagName()中的標簽名是不需要區分大小寫的。但對於XML而言,getElementByTagName()就會區分大小寫。

getElementsByName()

隻有HTMLDocument類型才有的方法,這個方法會返回帶有給定name特性的所有元素。
最常使用getElementsByName()方法的情況是取得單選按鈕,因為一般所有單選按鈕都具有相同的name特性,這才能確保幾個單選項中隻有一個被發送給瀏覽器。
我們可以使用如下代碼取得所有單選按鈕:

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

與getElementsByTagName()類似,getElementsByName()方法也會返回一個HTMLCollection。
但是對這裡取到的單選按鈕來說,namedItem()則隻會取得第一項,因為每一項的name特性都相同。

特殊集合

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

document.anchors:包含文檔中所有帶name屬性的< a >元素 document.applets:包含文檔中所有的< applet >元素,因為不再使用推薦這個元素,所以這個集合已經不建議使用瞭。 document.forms:包含文檔中所有的< form >元素 document.images:包含文檔中所有的< img >元素 document.links:包含文檔中所有帶href屬性的< a >元素

這個特殊集合始終都可以通過HTMLDocument對象訪問到,而且集合中的項也會隨著當前文檔內容的更新而更新。

文檔寫入

document對象有一個功能,即將輸出流寫入到網頁中。這個能力體現在下列4個方法中:

write()

writeln()

其中,write()和writeln()方法都接收一個字符串參數,即要寫入輸出流中的文本。

write()會原樣寫入 writeln()會在字符串的末尾添加一個換行符(\n)

在頁面被加載的過程中,可以使用這兩個方法向頁面中動態地加入內容。
此外,還可以使用這兩個方法動態地包含外部資源,例如js文件等。

在包含js文件時,註意不能直接包含字符串“< /script>”,因為這會導致該字符串被解釋為腳本塊的結束,它下面的代碼將無法執行。
為避免這個問題,隻需加入轉義字符\即可:

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

這樣,字符串””不會被當做外部標簽的關閉標簽。

前面的例子使用document.write( )在頁面被呈現的過程中直接向其中輸出瞭內容。
如果在文檔加載結束後再調用document.write(),那麼輸出的內容將會重寫整個頁面。

window.onload = function(){
    document.write("hello world");
}

在這個例子中,我們等到頁面完全加載之後才執行函數,這時字符串”hello world”會重寫整個頁面內容。

open()

close()

這兩個方法分別用於打開和關閉網頁的輸出流。如果實在頁面加載期間使用write()或writeln()方法,則不需要用到這兩個方法。

Element類型

Element類型用於表現XML或HTML元素,Element節點具有以下特征:

nodeType:值為1
nodeName:值為元素的標簽名
nodeValue:值為null
parentNode:可能是Document或Element
其子節點可能是Element、Text、Comment、ProcessiongInstruction、CDATASection

要訪問元素的標簽名,可以使用nodeName屬性,也可以使用tagName屬性;這兩個屬性會返回相同的值。

some text

var p = document.getElementById("myDiv");
p.tagName;  //"DIV"
p.nodeName == p.tagName; //true

在html中,標簽名始終都以全部大寫表示;而在XML中,標簽名則始終會與源代碼中的保持一致。假如不確定腳本將會在html或xml中執行,最好還是在比較之前將標簽名轉換為相同的大小寫形式。

if(element.tagName.toLowerCase() == "p"){ }

HTML元素

所有HTML元素都由HTMLElement類型或其子類型表示。HTMLElement繼承自Element並添加瞭一些屬性。添加的這些屬性分別對應於每個HTML元素中都存在的下列標準特性:

id
title:有關元素的附加說明信息,一般通過工具提示條顯示出來
lang:元素內容的語言代碼,很少使用
dir:語言的方向,值為“ltr”或“rtl”
className:與元素的class特性對應,沒有命名為class是因為class是ECMAscript的保留字。

上述這些屬性都可以用來取得或修改相應的特性值。

var p = document.getElementById("myDiv");
p.id; //"myDiv"
p.className; //"bd"
p.title = "some other text";
p.lang = "fr";

並不是對所有屬性的修改都會在頁面中直觀地表現出來。

對id、lang的修改對用戶而言是不可見的(如果沒有基於它們設置的css樣式)
對title的修改則隻會在鼠標移動到這個元素上時才會顯示出來
對dir的修改則會在屬性被重寫的那一刻,立即影響頁面中文本的左右
修改className時,如果新類關聯瞭與此前不同的css樣式,那麼就會立即應用新的樣式

取得特性

getAttribute()

用於取得特性值。
註意:傳遞給getAttribute()的特性名與實際的特性名相同,因此要想得到class特性值,應傳入“class”而不是“className”。
如果給定的特性名稱不存在,此方法返回null。

通過getAttribute()方法也可以取得自定義特性。

var value = p.getAttribute("my_special_attribute");

不過,特性的名稱是不區分大小寫的,即“ID”和“id”表示的是同一個特性。

另外,根據HTML5規范,自定義特性應該加上data-前綴以便驗證。

任何元素的特性也都可以直接通過DOM元素本身的對應屬性來訪問(如p.id)。但是自定義特性則不會有對應的屬性,隻能以getAttribute()來獲取。

有兩類特殊的特性,它們雖然有對應的屬性名,但屬性的值與通過getAttribute()返回的值並不相同。

第一類:style
在通過getAttribute()訪問時,返回的style特性值中包含的是CSS文本
通過屬性訪問則會返回一個對象
第二類:onclick這樣的事件處理程序。在元素上使用時,onclick特性中包含的是js代碼。
通過getAttribute()訪問,就會返回相應代碼的字符串
訪問onclick屬性,則會返回一個js函數(如果未在元素中指定相應特性則返回null)。

由於存在這些差別,開發過程中不經常使用getAttribute(),而是隻使用對象的屬性。在取得自定義特性值的時候才使用getAttribute方法。

設置特性

setAttribute()

這個方法接收兩個參數:要設置的特性名和值。

特性已經存在,則會以指定的值替換現有的值
特性不存在,會創建該屬性並設置相應的值

p.setAttribute("id","someOtherId");

通過setAttribute()方法既可以操作HTML特性也可以操作自定義特性。通過這個方法設置的特性名會被統一轉換為小寫形式,即“ID”會變成“id”。

因為所有特性都是屬性,所以直接給屬性賦值也可以設置特性的值:

p.id = "someOtherId";

不過,給元素添加一個自定義的屬性,該屬性不會自動成為元素的特性

p.myColor = "red";
p.getAttribute("myColor"); //null

總結一下元素特性與自定義特性:
* 在元素中設置特性,可以通過屬性獲得元素特性,卻不能獲得自定義特性,隻能用getAttribute()方法獲得。
* 設置元素的屬性,通過getAttribute()無法獲得自定義屬性對應的特性,而可以獲得元素特性。
即,元素特性和屬性是對應的,但自定義的卻無法自動在有屬性或特性的情況下添加對應的特性或屬性。

removeAttribute()

這個方法用於徹底刪除元素的特性。
調用這個方法不僅會清除特性的值,而且也會從元素中完全刪除特性。

attributes屬性

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

getNamedItem(name):返回nodeName屬性等於name的節點
removeNamedItem(name):從列表中移除nodeName屬性等於name的節點。
setNamedItem(node):向列表中添加節點,以節點的nodeName屬性為索引
item(pos):返回為與數字pos位置處的節點。

attributes屬性中包含一系列節點,每個節點的nodeName就是特性的名稱,節點的nodeValue就是特性的值。
要取得元素的id特性,可以使用以下代碼:

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

也可以使用方括號語法通過特性名稱訪問節點:

var id = element.attributes["id"].nodeValue;

也可以使用這種語法來設置特性的值,即先取得特性節點,然後再將其nodeValue設置為新值。

var id = element.attributes["id"].nodeValue = "someOtherId"

調用removeNamedItem()方法與在元素上調用removeAttribute()方法的效果相同——直接刪除具有給定名稱的特性。兩個方法唯一的區別就是,removeNamedItem()返回表示被刪除特性的Attr節點:

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

最後,setNamedItem()是個很不常用的方法,通過這個方法可以為元素添加一個新特性,但需要為它傳入一個特性節點。

一般來說,由於使用attributes的各種方法不夠方便,一般還是使用getAttribute(),setAttribute(),removeAttribute()比較多。

不過,要想遍歷元素的特性,attributes屬性倒是可以派上用場。

針對attributes對象中的特性,不同瀏覽器返回的順序不同。

創建元素

document.createElement()

這個方法可以創建新元素。隻接受一個參數,即要創建元素的標簽名。這個標簽名在html中不區分大小寫,而在xml中則是區分大小寫的。
此方法返回這個新元素的引用。

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

在使用createElement()方法創建新元素的同時,也為新元素設置瞭ownerDocument屬性。

新創建的元素未被添加到文檔樹中,可以使用appendChild,insertBefore、replaceChild等方法把新創建的元素添加到文檔中:

document.body.appendChild(p);

一旦將元素添加到文檔樹中,瀏覽器就會立即呈現該元素。此後,對這個元素所做的任何修改都會實時反映在瀏覽器中。

元素的子節點

元素可以有任意數目的子節點和後代節點,因為元素可以是其他元素的子節點。
元素的childNodes屬性中包含瞭它的所有子節點,這些子節點可能是元素、文本節點、註釋或處理指令。

不同瀏覽器在看到節點方面有差異:

  • item 1
  • item 2
  • item 3

對於以上代碼,如果ie解析則ul會有3個節點;其他瀏覽器則會看做7個子節點(3個li節點加上li元素之間的4個空白符)。但如果像下面這樣將元素間的空白符刪除,那麼所有瀏覽器都會返回相同數目的子節點。

  • item 1
  • item 2
  • item 3

元素也支持getElementsByTagName()方法,會返回符合條件的後代元素。

Text類型

文本節點由Text類型表示。純文本中可以包含轉義後的HTML字符,但不能包含HTML代碼。
Text節點具有以下特征:

nodeType:值為3
nodeName:值為“#text”
nodeValue:值為節點所包含的文本
parentNode:是一個Element
沒有子節點

可以通過nodeValue屬性或data屬性訪問Text節點中包含的文本,這兩個屬性中包含的值相同。對nodeValue修改也會通過data反映出來,反之亦然。使用下列方法可以操作節點中的文本:

appendData(text):將text添加到節點的末尾
deleteData(offset,count):從offset指定的位置開始刪除count個字符
insertData(offset,text):在offset指定的位置插入text
replaceData(offset,count,text):用text替換從offset指定的位置開始到offset+count為止處的文本。
splitText(offset):從offset指定的位置將當前文本節點分成兩個文本節點
substringData(offset,count):提取從offset指定的位置開始到offset+count為止處的字符串。

除瞭這些方法之外,文本節點還有一個length屬性,保存著節點中字符的數目。而且,nodeValue.length和data.length中也保存著同樣的值。

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

沒有內容,也就沒有文本節點

有空格,也就有一個文本節點

hello world

有內容,也就有一個文本節點

可以使用如下代碼來訪問這些文本子節點:

var textNode = p.firstChild;
var textNode = p.childNodes[0];

在取得文本節點的引用後,就可以像下面這樣來修改它瞭:

p.firstChild.nodeValue = "some other message";

如果這個文本節點當前存在於文檔樹中,那麼修改文本節點的結果就會立即得到反映。
另外,在修改文本節點時還要註意,此時的字符串會經過HTML編碼。也就是說,這是在向DOM文檔中插入文本之前,先對其進行HTML編碼的一種有效方式。

創建文本節點

document.createTextNode()

可以使用此方法創建新文本節點,這個方法接收一個參數,即要插入節點中的文本。
與設置已有文本節點的值一樣,作為參數的文本也將按照HTML的格式進行編碼。

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

在創建新文本節點的同時,也會為其設置ownerDocument屬性。
不過,除非把新節點添加到文檔樹中已經存在的節點中,否則我們不會在瀏覽器窗口中看到新節點。

一般情況下,每個元素隻有一個文本子節點。不過在某些情況下也可能包含多個文本子節點,如果兩個文本節點是相鄰的同胞節點,那麼這兩個節點中的文本就會連起來顯示,中間不會有空格。

規范化文本節點

DOM文檔中出現相鄰的同胞文本節點很容易導致混亂,因此就催生瞭一個能夠將相鄰文本節點合並的方法。

normalize()

這個方法由Node類型定義。
如果在一個包含兩個或多個文本節點的父元素上調用normalize()方法,則會將所有文本節點合並成一個節點。結果節點的nodeValue等於將合並前每個文本節點的nodeValue值拼接起來的值。

瀏覽器在解析文檔時永遠不會創建相鄰的文本節點。這種情況隻會作為執行DOM操作的結果出現。

分隔文本節點

Text類型提供瞭一個作用域normalize()相反的方法:

splitText()

這個方法會將一個文本節點分成兩個文本節點,即按照指定的位置分隔nodeValue值。原來的文本節點將會包含從開始到指定位置之前的內容,新文本節點將包含剩下的文本。
這個方法會返回一個新文本節點,該節點與原節點的parentNode相同。

var textNode = p.firstChild; //"hello world"
var newNode = textNode.splitText(5);
p.firstChild.nodeValue; //"hello"
newNode.nodeValue; //" world"
p.childNodes.length; //2

分隔文本節點是從文本節點中提取數據的一種常用DOM解析技術。

Comment類型

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

nodeType:值為8
nodeName:值為“#comment”
nodeValue:值是註釋的內容
parentNode:可能是Document或Element
沒有子節點

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

var p = document.getElementById("myDiv");
var comment = p.firstChild;
comment.data; //"a comment"

document.createComment()

使用此方法可以為其傳遞註釋文本也可以創建註釋節點。

var comment = p.createComment("a comment");

瀏覽器並不會識別標簽後面的註釋。如果要訪問註釋節點,一定要保證其是元素的後代。

</script>

發佈留言