除了 Document
類型以外,Element
類型就要算是 Web 編程中最經常使用的類型了。Element
類型用於表現 XML 或 HTML 元素,提供了對元素標籤名、子節點及特性的訪問。Element
節點具備如下特徵:javascript
nodeType
的值爲1;nodeName
的值爲元素的標籤名;nodeValue
的值爲 null
;parentNode
多是 Document
或 Element
;Element
、Text
、Comment
、ProcessingInstruction
、CDATASection
或 EntityReference
。要訪問元素的標籤名,可使用 nodeName
屬性,也可使用 tagName
屬性;這兩個屬性會返回相同的值(使用後者主要是爲了清晰起見)。如下面的元素爲例:java
複製代碼
能夠像下面這樣取得這個元素及其標籤名:node
var div = document.getElementById("myDiv");
console.log(div.tagName); // "DIV"
console.log(div.tagName === div.nodeName); // true複製代碼
這裏的元素標籤名是 div
,它擁有一個值爲 "myDiv"
的ID。但是,div.tagName
實際上輸出的是 "DIV"
而非 "div"
。在HTML中,標籤名始終都以所有大寫表示;而在 XML(有時候也包括 XHTML)中,標籤名則始終會與源代碼中的保持一致。假如你不肯定本身的腳本將會在 HTML 仍是 XML 文檔中執行,最好是在比較以前將標籤名轉換爲相同的大小寫形式,以下面的例子所示:git
// 不能這樣比較,很容易出錯!
if (element.tagName == "div"){
//在此執行某些操做
}
// 這樣最好(適用於任何文檔)
if (element.tagName.toLowerCase() == "div"){
//在此執行某些操做
}複製代碼
全部 HTML 元素都由 HTMLElement
類型表示,不是直接經過這個類型,也是經過它的子類型來表示。HTMLElement
類型直接繼承自 Element
並添加了一些屬性。添加的這些屬性分別對應於每一個 HTML 元素中都存在的下列標準特性。github
id
,元素在文檔中的惟一標識符。title
,有關元素的附加說明信息,通常經過工具提示條顯示出來。lang
,元素內容的語言代碼,不多使用。dir
,語言的方向,值爲 "ltr"
(left-to-right,從左至右)或 "rtl"
(right-to-left,從右至左),也不多使用。className
,與元素的 class
特性對應,即爲元素指定的 CSS 類。沒有將這個屬性命名爲 class
,是由於 class
是 JavaScript 的保留字。上述這些屬性均可以用來取得或修改相應的特性值。如下面的HTML元素爲例:算法
複製代碼
元素中指定的全部信息,均可以經過下列 JavaScript 代碼取得:編程
var div = document.getElementById("myDiv");
console.log(div.id); // "myDiv""
console.log(div.className); // "bd"
console.log(div.title); // "Body text"
console.log(div.lang); // "en"
console.log(div.dir); // "ltr"複製代碼
固然,像下面這樣經過爲每一個屬性賦予新的值,也能夠修改對應的每一個特性:瀏覽器
div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir ="rtl";複製代碼
並非對全部屬性的修改都會在頁面中直觀地表現出來。對 id
或 lang
的修改對用戶而言是透明不可見的(假設沒有基於它們的值設置的 CSS 樣式),而對 title
的修改則只會在鼠標移動到這個元素之上時纔會顯示出來。對 dir
的修改會在屬性被重寫的那一刻,當即影響頁面中文本的左、右對齊方式。修改 className
時,若是新類關聯了與此前不一樣的 CSS 樣式,那麼就會當即應用新的樣式。微信
每一個元素都有一或多個特性,這些特性的用途是給出相應元素或其內容的附加信息。操做特性的 DOM 方法主要有三個,分別是 getAttribute()
、setAttribute()
和 removeAttribute()
。這三個方法能夠針對任何特性使用,包括那些以 HTMLElement
類型屬性的形式定義的特性。來看下面的例子:app
var div = document.getElementById("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
。
經過 getAttribute()
方法也能夠取得自定義特性(即標準 HTML 語言中沒有的特性)的值,如下面的元素爲例:
複製代碼
這個元素包含一個名爲 my_special_attribute
的自定義特性,它的值是 "hello!"
。能夠像取得其餘特性同樣取得這個值,以下所示:
var value = div.getAttribute("my_special_attribute");複製代碼
不過,特性的名稱是不區分大小寫的,即 "ID"
和 "id"
表明的都是同一個特性。另外也要注意,根據 HTML5 規範,自定義特性應該加上 data-
前綴以便驗證。
任何元素的全部特性,也均可以經過 DOM 元素自己的屬性來訪問。固然,HTMLElement
也會有5個屬性與相應的特性一一對應。不過,只有公認的(非自定義的)特性纔會以屬性的形式添加到 DOM 對象中。如下面的元素爲例:
複製代碼
由於 id
和 align
在 HTML 中是 div
的公認特性,所以該元素的 DOM 對象中也將存在對應的屬性。不過,自定義特性 my_special_attribute
在 Safari、Opera、Chrome 及 Firefox 中是不存在的;但 IE 卻會爲自定義特性也建立屬性,以下面的例子所示:
console.log(div.id); // "myDiv"
console.log(div.my_special_attribute); // undefined(IE除外)
console.log(div.align); // "left"複製代碼
有兩類特殊的特性,它們雖然有對應的屬性名,但屬性的值與經過 getAttribute()
返回的值並不相同。第一類特性就是 style
,用於經過 CSS 爲元素指定樣式。在經過 getAttribute()
訪問時,返回的 style
特性值中包含的是CSS文本,而經過屬性來訪問它則會返回一個對象。因爲 style
屬性是用於以編程方式訪問元素樣式的,所以並無直接映射到 style
特性。
第二類不同凡響的特性是 onclick
這樣的事件處理程序。當在元素上使用時,onclick
特性中包含的是 JavaScript 代碼,若是經過 getAttribute()
訪問,則會返回相應代碼的字符串。而在訪問 onclick
屬性時,則會返回一個 JavaScript 函數(若是未在元素中指定相應特性,則返回 null)。這是由於 onclick
及其餘事件處理程序屬性自己就應該被賦予函數值。
因爲存在這些差異,在經過 JavaScript 以編程方式操做 DOM 時,開發人員常常不使用 getAttribute()
,而是隻使用對象的屬性。只有在取得自定義特性值的狀況下,纔會使用 getAttribute()
方法。
與 getAttribute()
對應的方法是 setAttribute()
,這個方法接受兩個參數:要設置的特性名和值。若是特性已經存在,setAttribute()
會以指定的值替換現有的值;若是特性不存在,setAttribute()
則建立該屬性並設置相應的值。來看下面的例子:
div.setAttribute("id", "someOtherId");
div.setAttribute("class", "ft");
div.setAttribute("title", "Some other text");
div.setAttribute("lang","fr");
div.setAttribute("dir", "rtl");複製代碼
經過 setAttribute()
方法既能夠操做HTML特性也能夠操做自定義特性。經過這個方法設置的特性名會被統一轉換爲小寫形式,即 "ID"
最終會變成 "id"
。
由於全部特性都是屬性,因此直接給屬性賦值能夠設置特性的值,以下所示。
div.id = "someOtherId";
div.align = "left";複製代碼
不過,像下面這樣爲 DOM 元素添加一個自定義的屬性,該屬性不會自動成爲元素的特性。
div.mycolor = "red";
console.log(div.mycolor); // "red"
console.log(div.getAttribute("mycolor")); // null(IE除外)複製代碼
這個例子添加了一個名爲 mycolor
的屬性並將它的值設置爲 "red"
。在大多數瀏覽器中,這個屬性都不會自動變成元素的特性,所以想經過 getAttribute()
取得同名特性的值,結果會返回 null
。但是,自定義屬性在 IE 中會被看成元素的特性,反之亦然。
要介紹的最後一個方法是 removeAttribute()
,這個方法用於完全刪除元素的特性。調用這個方法不只會清除特性的值,並且也會從元素中徹底刪除特性,以下所示:
div.removeAttribute("class");複製代碼
這個方法並不經常使用,但在序列化 DOM 元素時,能夠經過它來確切地指定要包含哪些特性。
使用 document.createElement()
方法能夠建立新元素。這個方法只接受一個參數,即要建立元素的標籤名。這個標籤名在 HTML 文檔中不區分大小寫。例如,使用下面的代碼能夠建立一個 div
元素。
var div = document.createElement("div");複製代碼
在使用 createElement()
方法建立新元素的同時,也爲新元素設置了 ownerDocuemnt
屬性。此時,還能夠操做元素的特性,爲它添加更多子節點,以及執行其餘操做。來看下面的例子。
div.id = "myNewDiv";
div.className = "box";複製代碼
在新元素上設置這些特性只是給它們賦予了相應的信息。因爲新元素還沒有被添加到文檔樹中,所以設置這些特性不會影響瀏覽器的顯示。要把新元素添加到文檔樹,可使用 appendChild()
、insertBefore()
或 replaceChild()
方法。下面的代碼會把新建立的元素添加到文檔的 元素中。
document.body.appendChild(div);複製代碼
一旦將元素添加到文檔樹中,瀏覽器就會當即呈現該元素。此後,對這個元素所做的任何修改都會實時反映在瀏覽器中。
元素能夠有任意數目的子節點和後代節點,由於元素能夠是其餘元素的子節點。元素的 childNodes
屬性中包含了它的全部子節點,這些子節點有多是元素、文本節點、註釋或處理指令。不一樣瀏覽器在看待這些節點方面存在顯著的不一樣,如下面的代碼爲例。
若是是 IE8 來解析這些代碼,那麼
元素。但若是是在其餘瀏覽器中,
元素都會有7個元素,包括3個
元素和4個文本節點(表示
元素之間的空白符)。若是像下面這樣將元素間的空白符刪除,那麼全部瀏覽器都會返回相同數目的子節點。
對於這段代碼,
childNodes
屬性遍歷子節點,那麼必定不要忘記瀏覽器間的這一差異。這意味着在執行某項操做之前,一般都要先檢查一下
nodeTpye
屬性,以下面的例子所示。
for (var i=0, len=element.childNodes.length; i < len; i++){
if (element.childNodes[i].nodeType == 1){
//執行某些操做
}
}複製代碼
這個例子會循環遍歷特定元素的每個子節點,而後只在子節點的 nodeType
等於1(表示是元素節點)的狀況下,纔會執行某些操做。
若是想經過某個特定的標籤名取得子節點或後代節點該怎麼辦呢?實際上,元素也支持 getElementsByTagName()
方法。在經過元素調用這個方法時,除了搜索起點是當前元素以外,其餘方面都跟經過 document
調用這個方法相同,所以結果只會返回當前元素的後代。例如,要想取得前面
元素,可使用下列代碼。
var ul = document.getElementById("myList");
var items = ul.getElementsByTagName("li");複製代碼
要注意的是,這裏
元素也都會返回。
文本節點由 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!複製代碼
上面代碼給出的第一個
元素中雖然只包含一個空格,但仍然有一個文本子節點;文本節點的
nodeValue
值是一個空格。第三個
div
也有一個文本節點,其
nodeValue
的值爲
"Hello World!"
。可使用如下代碼來訪問這些文本子節點。
var textNode = div.firstChild; // 或者div.childNodes[0]複製代碼
在取得了文本節點的引用後,就能夠像下面這樣來修改它了。
div.firstChild.nodeValue = "Some other message";複製代碼
若是這個文本節點當前存在於文檔樹中,那麼修改文本節點的結果就會當即獲得反映。另外,在修改文本節點時還要注意,此時的字符串會通過 HTML(或XML,取決於文檔類型)編碼。換句話說,小於號、大於號或引號都會像下面的例子同樣被轉義。
// 輸出結果是"Some other message"
div.firstChild.nodeValue = "Some other message";複製代碼
應該說,這是在向 DOM 文檔中插入文本以前,先對其進行 HTML 編碼的一種有效方式。
在 IE八、Firefox、Safari、Chrome 和 Opera中,能夠經過腳本訪問
Text
類型的構造函數和原型。
可使用 document.createTextNode()
建立新文本節點,這個方法接受一個參數——要插入節點中的文本。與設置已有文本節點的值同樣,做爲參數的文本也將按照 HTML 或 XML 的格式進行編碼。
var textNode = document.createTextNode("Hello world!");複製代碼
在建立新文本節點的同時,也會爲其設置 ownerDocument
屬性。不過,除非把新節點添加到文檔樹中已經存在的節點中,不然咱們不會在瀏覽器窗口中看到新節點。下面的代碼會建立一個
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
document.body.appendChild(element);複製代碼
這個例子建立了一個新
"message"
的
class
特性。而後,又建立了一個文本節點,並將其添加到前面建立的元素中。最後一步,就是將這個元素添加到了文檔的
元素中,這樣就能夠在瀏覽器中看到新建立的元素和文本節點了。
通常狀況下,每一個元素只有一個文本子節點。不過,在某些狀況下也可能包含多個文本子節點,以下面的例子所示。
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("Yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);複製代碼
若是兩個文本節點是相鄰的同胞節點,那麼這兩個節點中的文本就會連起來顯示,中間不會有空格。
DOM 文檔中存在相鄰的同胞文本節點很容易致使混亂,由於分不清哪一個文本節點表示哪一個字符串。另外,DOM 文檔中出現相鄰文本節點的狀況也不在少數,因而就催生了一個可以將相鄰文本節點合併的方法。這個方法是由 Node
類型定義的(於是在全部節點類型中都存在),名叫 normalize()
。若是在一個包含兩個或多個文本節點的父元素上調用 normalize()
方法,則會將全部文本節點合併成一個節點,結果節點的 nodeValue
等於將合併前每一個文本節點的 nodeValue
值拼接起來的值。來看一個例子。
var element = document.createElement("div");
element.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!"複製代碼
瀏覽器在解析文檔時永遠不會建立相鄰的文本節點。這種狀況只會做爲執行DOM操做的結果出現。
Text
類型提供了一個做用與 normalize()
相反的方法 splitText()
。這個方法會將一個文本節點分紅兩個文本節點,即按照指定的位置分割 nodeValue
值。原來的文本節點將包含從開始到指定位置以前的內容,新文本節點將包含剩下的文本。這個方法會返回一個新文本節點,該節點與原節點的 parentNode
相同。
註釋在 DOM 中是經過 Comment
類型來表示的。Comment
節點具備下列特徵:
nodeType
的值爲8;nodeName
的值爲 "#comment"
;nodeValue
的值是註釋的內容;parentNode
多是 Document
或 Element
;Comment
類型與 Text
類型繼承自相同的基類,所以它擁有除splitText()
以外的全部字符串操做方法。與 Text
類型類似,也能夠經過 nodeValue
或 data
屬性來取得註釋的內容。
註釋節點能夠經過其父節點來訪問,如下面的代碼爲例。
複製代碼
在此,註釋節點是
var div = document.getElementById("myDiv");
var comment = div.firstChild;
console.log(comment.data); // "A comment"複製代碼
另外,使用 document.createComment()
併爲其傳遞註釋文本也能夠建立註釋節點,以下面的例子所示。
var comment = document.createComment("A comment ");複製代碼
顯然,開發人員不多會建立和訪問註釋節點,由於註釋節點對算法鮮有影響。此外,瀏覽器也不會識別位於 標籤後面的註釋。若是要訪問註釋節點,必定要保證它們位於
和
之間。
元素的特性在 DOM 中以 Attr
類型來表示。在全部瀏覽器中(包括 IE8),均可以訪問 Attr
類型的構造函數和原型。從技術角度講,特性就是存在於元素的 attributes
屬性中的節點。特性節點具備下列特徵:
nodeType
的值爲11;nodeName
的值是特性的名稱;nodeValue
的值是特性的值;parentNode
的值爲 null
;Text
或 EntityReference
。儘管它們也是節點,但特性卻不被認爲是 DOM 文檔樹的一部分。開發人員最常使用的是 getAttribute()
、setAttribute()
和 remveAttribute()
方法,不多直接引用特性節點。
Attr
對象有3個屬性:name
、value
和 specified
。其中,name
是特性名稱(與 nodeName
的值相同),value
是特性的值(與 nodeValue
的值相同),而 specified
是一個布爾值,用以區別特性是在代碼中指定的,仍是默認的。
使用 document.createAttribute()
並傳入特性的名稱能夠建立新的特性節點。例如,要爲元素添加 align
特性,可使用下列代碼:
var attr = document.createAttribute("align");
attr.value = "left";
element.setAttributeNode(attr);
console.log(element.attributes["align"].value); // "left"
console.log(element.getAttributeNode("align").value); // "left"
console.log(element.getAttribute("align")); // "left"複製代碼
添加特性以後,能夠經過下列任何方式訪問該特性:attributes
屬性、getAttributeNode()
方法以及 getAttribute()
方法。其中,attributes
和 getAttributeNode()
都會返回對應特性的 Attr
節點,而 getAttribute()
則只返回特性的值。
不少時候,DOM 操做都比較簡明,所以用 JavaScript 生成那些一般本來是用 HTML 代碼生成的內容並不麻煩。不過,也有一些時候,操做 DOM 並不像表面上看起來那麼簡單。因爲瀏覽器中充斥着隱藏的陷阱和不兼容問題,用 JavaScript 代碼處理 DOM 的某些部分要比處理其餘部分更復雜一些。
使用
建立這個 DOM 節點的代碼以下所示:
function loadScript(url){
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}複製代碼
下面是調用這個函數的示例:
loadScript("client.js");複製代碼
另外一種指定 JavaScript 代碼的方式是行內方式,以下面的例子所示:
function sayHi(){
alert("hi");
}
複製代碼
從邏輯上講,下面操做的 DOM 代碼是有效的:
var script = document.createElement("script");
script.type = "text/javascript";
script.appendChild(document.createTextNode("function sayHi(){alert('hi');}"));
document.body.appendChild(script);複製代碼
在 Firefox、Safari、Chrome 和 Opera 中,這些 DOM 代碼能夠正常運行。但在 IE 中,則會致使錯誤。IE 將 var d = document.getElementById("t"); document.writeln(d.firstChild.innerHTML); // ??? document.writeln(d.lastChild.innerHTML); // ???
aaa
bbb
ccc
var d = document.getElementById("t");
document.writeln(d.childNodes[1].innerHTML); // ???
document.writeln(d.parentNode.getAttribute("name")); // ???
複製代碼
aaa
bbb
ccc
var d = document.getElementById("t").childNodes[1];
document.writeln(d.nextSibling.innerHTML); // ???
document.writeln(d.previousSibling.innerHTML); // ???
複製代碼
var t = document.getElementById("t");
console.log(t.class); // ???
console.log(t.getAttribute("class")); // ???
console.log(t.className); // ???
console.log(t.getAttribute("className")); // ???
console.log(t.style); // ???
console.log(t.getAttribute("style")); // ???
console.log(t.style.background); // ???
console.log(t.getAttribute("style.background")); // ???
console.log(t.wife); // ???
console.log(t.getAttribute("wife")); // ???
console.log(t.onclick); // ???
console.log(t.getAttribute("onclick")); // ???
複製代碼
關注微信公衆號「劼哥舍」回覆「答案」,獲取關卡詳解。
關注 github.com/stone0090/j…,獲取最新動態。