參考書(shū):《JavaScript高級(jí)程序設(shè)計(jì)》
知識(shí)點(diǎn)前提:什么是節(jié)點(diǎn)
Node類型
DOM1級(jí)定義了一個(gè)Node接口怕轿,該接口將DOM中所有節(jié)點(diǎn)類型實(shí)現(xiàn)啸罢。Node接口在JavaScript中是作為Node類型實(shí)現(xiàn)的丑孩;除了IE之外,在其他所有瀏覽器都可以訪問(wèn)到這個(gè)類型。
nodeType
只讀
每個(gè)節(jié)點(diǎn)都有一個(gè)nodeType屬性窟感,有用于表明節(jié)點(diǎn)的類型。節(jié)點(diǎn)類型由在Node類型中定義的下列12個(gè)數(shù)值常量表示歉井,任何節(jié)點(diǎn)類型必占據(jù)一種柿祈。
- 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);
test1
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h");
console.log(h.nodeType); // 1
if (h.nodeType === Node.ELEMENT_NODE) console.log(true); // true
</script>
</html>
使用Node類型常量來(lái)比較。
由于IE沒(méi)有公開(kāi)Node類型函數(shù)哩至,因此上面代碼在IE中會(huì)導(dǎo)致錯(cuò)誤躏嚎。為確保瀏覽器兼容,最好將nodeType屬性與數(shù)字進(jìn)行比較
test2
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h");
if (h.nodeType === 1) console.log(true); // true
</script>
</html>
不是所有節(jié)點(diǎn)都會(huì)受到瀏覽器支持菩貌,最常用的就是元素和文本節(jié)點(diǎn)卢佣。
nodaName | nodeValue
nodaName
只讀
nodaValue
可寫(xiě)可讀
使用nodeName和nodeValue可以具體了解節(jié)點(diǎn)信息,這兩個(gè)屬性的值完全取決于節(jié)點(diǎn)的類型箭阶。
可以檢測(cè)節(jié)點(diǎn)類型后在使用其屬性虚茶,下圖列出了不同節(jié)點(diǎn)類型的nodeValue的返回值。
所以仇参,對(duì)于element這樣的節(jié)點(diǎn)媳危,返回值都為null。
test3
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h");
console.log(h.nodeName+" | "+h.nodeValue);
// H1 | null
</script>
</html>
可以這樣
test4
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h").getAttributeNode("id");
console.log(h); // id [object attr]
console.log(h.nodeValue); // h
</script>
</html>
使用getAttributeNode方法冈敛,輸入屬性名待笑,返回其attribute節(jié)點(diǎn)對(duì)象,然后使用NodeValue方法抓谴,返回值就為實(shí)際屬性值暮蹂。
節(jié)點(diǎn)關(guān)系
文檔中所有的節(jié)點(diǎn)之間都存在這樣或者那樣的關(guān)系。節(jié)點(diǎn)間的各種關(guān)系可以用傳統(tǒng)的家族關(guān)系來(lái)描述癌压,每個(gè)節(jié)點(diǎn)存在childNodes屬性仰泻,其中保存著一個(gè)Nodelist對(duì)象,是一種類數(shù)組對(duì)象滩届,保存一組有序的節(jié)點(diǎn)集侯,可以通過(guò)位置來(lái)訪問(wèn)這些節(jié)點(diǎn)。
雖然可以使用方括號(hào)語(yǔ)法來(lái)訪問(wèn)Nodelist的值,而且這個(gè)對(duì)象也有l(wèi)ength屬性棠枉,但它并不是Array的實(shí)例浓体。Nodelist對(duì)象的獨(dú)特之處在于,它實(shí)際上是基于DOM結(jié)構(gòu)動(dòng)態(tài)執(zhí)行查詢的結(jié)果辈讶,因此DOM結(jié)構(gòu)的變化能夠自動(dòng)反映在Nodelist對(duì)象中命浴。
我們常說(shuō),Nodelist是有生命贱除、有呼吸的對(duì)象生闲,而不是在我們第一次訪問(wèn)它們的某個(gè)瞬間拍攝下來(lái)的一張快照。
test5*
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p>i'm P</p>
<h1>i'm H1</h1>
</div>
</body>
<script>
var html = document.getElementsByTagName("html")[0];
console.log(html);
</script>
</html>
可以看到其中childNodes屬性里月幌,Nodelist對(duì)象的傳承碍讯,包含了從根目錄下的所有節(jié)點(diǎn)。
其中扯躺,#div的Nodelist里捉兴,是否發(fā)現(xiàn)了有5個(gè)節(jié)點(diǎn),但實(shí)際上源代碼只有2個(gè)元素啊缅帘,其實(shí)[0],[2],[4]
所代表的#text是我們?cè)创a換行時(shí)留下的空位轴术,在Nodelist里就用#text來(lái)表示了。
<div id="div"><p>i'm P</p><h1>i'm H1</h1></div>
修改后钦无,將只有兩個(gè)element逗栽。
注意,length屬性表示的是訪問(wèn)Nodelist的那一刻失暂,其中包含的節(jié)點(diǎn)數(shù)量彼宠。
對(duì)于JS函數(shù)內(nèi)部類數(shù)組對(duì)象arguments對(duì)象使用Array.prototype.slice()
方法可以將其轉(zhuǎn)換為數(shù)組。而采用同樣的方法弟塞,也可以將Nodelist對(duì)象轉(zhuǎn)換為數(shù)組凭峡。
test6
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p>i'm P</p>
<h1>i'm H1</h1>
</div>
</body>
<script>
var div = document.getElementById("div");
// 將Nodelist對(duì)象轉(zhuǎn)換為數(shù)組
var arrayOfNodes = Array.prototype.slice.call(div.childNodes,0);
// 修改數(shù)組不會(huì)反映到Nodelist
arrayOfNodes.splice(1,0,arrayOfNodes[3]);
console.log(div.childNodes);
</script>
</html>
為方便演示,縮小范圍决记,只抽取了#div做演示摧冀,
白箭頭即為轉(zhuǎn)換后的真數(shù)組,對(duì)轉(zhuǎn)換后數(shù)組進(jìn)行了一次數(shù)組splice增添節(jié)點(diǎn)操作系宫,那么索昂,是否會(huì)映射到Nodelis上呢?
紅箭頭是Nodelist對(duì)象扩借,看看它椒惨,依然不變。
當(dāng)然潮罪,通過(guò)Nodelist對(duì)象方法添加是可以康谆,后續(xù)~
對(duì)于剛才的Nodelist對(duì)象轉(zhuǎn)換數(shù)組方法领斥,由于在IE8及更早版本將Nodelist實(shí)現(xiàn)為一個(gè)COM對(duì)象,而我們不能像使用JavaScript對(duì)象那樣使用這種對(duì)象沃暗,因此原方法會(huì)導(dǎo)致錯(cuò)誤月洛,要想在IE中Nodelist轉(zhuǎn)換為數(shù)組,必須手動(dòng)枚舉所有成員描睦。
下列代碼在所有瀏覽器均可運(yùn)行:
test7
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p>i'm P</p>
<h1>i'm H1</h1>
</div>
</body>
<script>
function convertToArray(nodes) {
var array = null;
try {
array = Array.prototype.slice.call(nodes,0); // 針對(duì)非IE瀏覽器
} catch (ex) {
array = new Array();
for (var i=0,len=nodes.length;i<len;i++) {
array.push(nodes[i]);
}
}e
return array;
}
var div = document.getElementById("div");
console.log(convertToArray(div.childNodes));
</script>
</html>
Array(5) [#text, p, #text, h1, #text]
函數(shù)首先嘗試了創(chuàng)建數(shù)組的最簡(jiǎn)單的方式膊存。如果導(dǎo)致錯(cuò)誤(說(shuō)明在IE8及更早版本中)导而,則通過(guò)try-catch來(lái)捕獲錯(cuò)誤忱叭,然后手動(dòng)創(chuàng)建數(shù)組。
每個(gè)節(jié)點(diǎn)上都有一個(gè)parentNode屬性今艺,該屬性指向文檔樹(shù)中的父節(jié)點(diǎn)韵丑。包含在childNodes列表中的所有節(jié)點(diǎn)都具有相同的父節(jié)點(diǎn),因此它們的parentNode屬性都指向同一個(gè)節(jié)點(diǎn)虚缎。此外撵彻,包含在childNodes列表中的每個(gè)節(jié)點(diǎn)相互之間都是同胞節(jié)點(diǎn)(siblingNode)。通過(guò)使用列表中的每個(gè)節(jié)點(diǎn)的previousSibling和nextSibling屬性实牡,可以訪問(wèn)同一列表中的其他節(jié)點(diǎn)陌僵。
關(guān)于節(jié)點(diǎn)關(guān)系,主要就是子節(jié)點(diǎn)创坞、節(jié)點(diǎn)碗短、父節(jié)點(diǎn)之間的定位關(guān)系。
test8
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1></div>
</body>
<script>
var div = document.getElementById("div");
var p = document.getElementById("p");
var h = document.getElementById("h1");
console.log(div.firstChild); // 選中#div的第一個(gè)子元素:p#p
console.log(div.lastChild); // 選中#div的最后一個(gè)子元素:h1#h1
console.log(p.nextSibling); // 選中#p的下一個(gè)兄弟元素:h1#h1
console.log(h.previousSibling); // 選中#h1的上一個(gè)兄弟元素:p#p
console.log(p.parentNode); // 選中#p的父元素:div#div
</script>
</html>
在反映這些關(guān)系的所有屬性當(dāng)中题涨,childNodes屬性與其他屬性相比更方便一些偎谁,因?yàn)橹豁毷褂煤?jiǎn)單的關(guān)系指針,就可以通過(guò)它訪問(wèn)文檔樹(shù)中的任何節(jié)點(diǎn)纲堵。另外巡雨,hasChildNodes()也是一個(gè)非常有用的方法,這個(gè)方法在節(jié)點(diǎn)包含一或多個(gè)子節(jié)點(diǎn)的情況下返回true席函;應(yīng)該說(shuō)铐望,這是比查詢childNodes列表的length屬性更簡(jiǎn)單的方法。
所有節(jié)點(diǎn)都有的最后一個(gè)屬性時(shí)ownerDocument茂附,該屬性指向表示整個(gè)文檔的文檔節(jié)點(diǎn)正蛙。這種關(guān)系表示的是任何節(jié)點(diǎn)都屬于它所在的文檔,任何節(jié)點(diǎn)都不能同時(shí)存在與兩個(gè)或多個(gè)文檔中何之。通過(guò)這個(gè)屬性跟畅,我們可以不必在節(jié)點(diǎn)層次中通過(guò)層層回溯到達(dá)頂端,而是可以直接訪問(wèn)文檔節(jié)點(diǎn)溶推。
操作節(jié)點(diǎn)
因?yàn)殛P(guān)系指針都是只讀的徊件,所以DOM提供了一些操作節(jié)點(diǎn)的方法奸攻。其中,最常用的方法時(shí)appendChild()虱痕,用于向childNodes列表的末尾添加一個(gè)節(jié)點(diǎn)睹耐。添加節(jié)點(diǎn)后,childNodes的新增節(jié)點(diǎn)部翘、父節(jié)點(diǎn)及以前的最后一個(gè)子節(jié)點(diǎn)的關(guān)系指針都會(huì)相應(yīng)的得到更新(因?yàn)閏hildNodes屬性里的Nodelist對(duì)象更新了)硝训。更新完成后,appendChild()返回新增的節(jié)點(diǎn)新思。
test9
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p id="p">i'm P</p>
<h1 id="h1">i'm H1</h1>
</div>
</body>
<script>
var h6 = document.createElement("h6");
var div = document.getElementById("div");
var returnNode = div.appendChild(h6);
console.log(returnNode === h6); // true
console.log(div.lastChild === h6); // true
</script>
</html>
如果傳入到appendChild()中的節(jié)點(diǎn)已經(jīng)是文檔中的一部分了窖梁,那結(jié)果就是將該節(jié)點(diǎn)從原來(lái)的位置轉(zhuǎn)移到新位置。即使可以將DOM數(shù)看成是由一系列指針連接起來(lái)的 夹囚,但任何DOM節(jié)點(diǎn)也不能同時(shí)出現(xiàn)在文檔中的多個(gè)位置上纵刘。因此,如果在調(diào)用appenChild()時(shí)傳入了父節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn)荸哟,那么該節(jié)點(diǎn)就會(huì)成為父節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)假哎。
test10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p id="p">i'm P</p>
</div>
</body>
<script>
var div = document.getElementById("div");
var returnNode = div.appendChild(div.firstChild);
console.log(returnNode === div.firstChild); // false
console.log(returnNode === div.lastChild); // true
</script>
</html>
如果需要把節(jié)點(diǎn)放在childNodes列表中某個(gè)特定的位置上,而不是放在末尾鞍历,那么可以使用inserBefore()方法舵抹。這個(gè)方法接受兩個(gè)參數(shù):要插入的節(jié)點(diǎn)和作為參照的節(jié)點(diǎn)。插入節(jié)點(diǎn)后劣砍,被插入的節(jié)點(diǎn)會(huì)變成參照節(jié)點(diǎn)的前一個(gè)同胞節(jié)點(diǎn)(preivousSibling)惧蛹,同時(shí)被方法返回,如果參照節(jié)點(diǎn)是null秆剪,則insertBefore()與appendChild()執(zhí)行相同的操作赊淑。
test11
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1></div>
</body>
<script>
var div = document.getElementById("div");
var h1 = document.getElementById("h1");
var p = document.getElementById("p");
console.log(div);
// 創(chuàng)建的新節(jié)點(diǎn)h6
var h6 = document.createElement("h6");
var text = document.createTextNode("i'm h6");
h6.appendChild(text);
// 將新創(chuàng)建的h6節(jié)點(diǎn)插入到h1之前
div.insertBefore(h6,h1);
console.log(h1.previousSibling === h6); // true
// p > h6 > h1
// 將h1插入到p之前,同時(shí)h6位置為最后一個(gè)子節(jié)點(diǎn)
div.insertBefore(h6,p);
console.log(p.previousSibling === h6); // true
console.log(div.firstChild === h6); // true
console.log(div.lastChild === h1); // true
</script>
</html>
注意仅讽,如果將代碼換行陶缺,保證可讀性的同時(shí),會(huì)使Nodelist對(duì)象增加若干的#text
節(jié)點(diǎn)洁灵,這是換行導(dǎo)致的饱岸。
如若要替換節(jié)點(diǎn)可使用replaceChild()方法
方法接受兩個(gè)參數(shù):要插入的節(jié)點(diǎn),要替換的節(jié)點(diǎn)徽千。要替換的節(jié)點(diǎn)將由這個(gè)方法返回并從文檔樹(shù)中移除苫费,同時(shí)插入的節(jié)點(diǎn)占據(jù)其位置。
test12
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1>
</div>
</body>
<script>
var div = document.getElementById("div");
var h1 = document.getElementById("h1");
var p = document.getElementById("p");
// 創(chuàng)建一個(gè)新節(jié)點(diǎn)h3
var h3 = document.createElement("h3");
h3.appendChild(document.createTextNode("i'm h3"));
// 替換p節(jié)點(diǎn)
var returnNode = div.replaceChild(h3,p);
console.log(returnNode); // p#p
</script>
</html>
使用replaceChild()插入一個(gè)節(jié)點(diǎn)時(shí)双抽,該節(jié)點(diǎn)所有關(guān)系指針都會(huì)從被它替換的節(jié)點(diǎn)賦值過(guò)來(lái)百框,盡管從技術(shù)上講,被替換的節(jié)點(diǎn)還在文檔中牍汹,但它在文檔中已經(jīng)沒(méi)有了自己的位置铐维。
如果只想移除而非替換節(jié)點(diǎn)柬泽,可以使用removeChild()方法。
方法接受一個(gè)參數(shù)嫁蛇,即要移除的節(jié)點(diǎn)锨并。被移除的節(jié)點(diǎn)將成為方法的返回值。
test13
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1>
</div>
</body>
<script>
var div = document.getElementById("div");
var h1 = document.getElementById("h1");
var p = document.getElementById("p");
// 創(chuàng)建一個(gè)新節(jié)點(diǎn)h3
var h3 = document.createElement("h3");
h3.appendChild(document.createTextNode("i'm h3"));
// 替換p節(jié)點(diǎn)
var returnNode = div.replaceChild(h3,p);
console.log(returnNode); // p#p
// 移除第一個(gè)節(jié)點(diǎn)
var formerFirstChild = div.removeChild(div.firstChild);
console.log(formerFirstChild); // h3
console.log(div.firstChild); // h1#h1
</script>
</html>
有兩個(gè)方法是所有類型節(jié)點(diǎn)都有的睬棚。
-
cloneNode()
-
normalize()
cloneNode()方法接受一個(gè)布爾值參數(shù)第煮,表示是否執(zhí)行深復(fù)制。參數(shù)為true情況下抑党,執(zhí)行深復(fù)制包警,也就是復(fù)制節(jié)及其整個(gè)子節(jié)點(diǎn)樹(shù);在參數(shù)為false情況下新荤,執(zhí)行潛復(fù)制揽趾,即只復(fù)制節(jié)點(diǎn)本身台汇。復(fù)制后返回的節(jié)點(diǎn)副本屬于文檔所有苛骨,但并沒(méi)有為它指定父節(jié)點(diǎn)。因此這個(gè)節(jié)點(diǎn)副本就成為了一個(gè)“孤兒”苟呐,除非通過(guò)appedChild痒芝,insertBefore,replaceChild將它添加到文檔中牵素。
test14
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
</body>
<script>
var ul = document.getElementsByTagName("ul")[0];
var deepList = ul.cloneNode(true);
var shallow = ul.cloneNode(false);
console.log(deepList.childNodes.length); // 7 4個(gè)空隙严衬,三個(gè)元素,長(zhǎng)度為7
console.log(shallow.childNodes.length); // 0
</script>
</html>
cloneNode()不會(huì)賦值添加到DOM節(jié)點(diǎn)中的JavaScript屬性笆呆,例如事件處理程序等请琳,這個(gè)方法只復(fù)制特性、子節(jié)點(diǎn)(true)赠幕,其他一切都不會(huì)復(fù)制俄精。
normalize()
這個(gè)方法唯一作用就是處理文檔樹(shù)中的文本節(jié)點(diǎn)。
由于解析器的實(shí)現(xiàn)或者DOM操作等原因榕堰,可能會(huì)出現(xiàn)文本節(jié)點(diǎn)或者不包含文本竖慧,或者接連出現(xiàn)兩個(gè)文本節(jié)點(diǎn)的情況。
當(dāng)在某個(gè)節(jié)點(diǎn)上調(diào)用這個(gè)方法時(shí)逆屡,就會(huì)在該節(jié)點(diǎn)的后代節(jié)點(diǎn)中查找上述兩種情況圾旨。如果找到空白文本節(jié)點(diǎn),則刪除它魏蔗,如果找到相鄰的文本節(jié)點(diǎn)砍的,則將它們合并為一個(gè)文本節(jié)點(diǎn)。
Document類型
JavaScript通過(guò)Document類型表示文檔莺治。在瀏覽器匯總廓鞠,document對(duì)象是HTMLDocument(繼承自Document類型)的一個(gè)實(shí)例味混,表示整個(gè)HTML頁(yè)面。而且诫惭,document對(duì)象是window對(duì)象的一個(gè)屬性翁锡,因此可以將其作為全局對(duì)象來(lái)訪問(wèn)。
window.document === document // true
Document節(jié)點(diǎn)具有下列特征:
-
nodeType的值為:9
-
nodeName的值為:"#document"
-
nodeValue的值為:null
-
parentNode的值為:null
-
其子節(jié)點(diǎn)可能是一個(gè)DocumentType(最多一個(gè))夕土、Element(最多一個(gè))馆衔、ProcessingInstruction或Comment。
Document類型可以表示HTML頁(yè)面或者其他基于XML的文檔怨绣。不過(guò)角溃,最常見(jiàn)的應(yīng)用還是作為HTMLDocument實(shí)例的document對(duì)象。通過(guò)這個(gè)文檔對(duì)象篮撑,不僅可以取得與頁(yè)面有關(guān)的信息减细,而且還能操作頁(yè)面的外觀及其底層結(jié)構(gòu)。
1.文檔的子節(jié)點(diǎn)
雖然DOM標(biāo)準(zhǔn)規(guī)定Document節(jié)點(diǎn)的子節(jié)點(diǎn)可以是DocumentType赢笨、Element未蝌、ProcessingIn-struction或Comment,但還有兩個(gè)內(nèi)置的訪問(wèn)其子節(jié)點(diǎn)的快捷方式茧妒。第一個(gè)就是documentElement屬性萧吠,該屬性始終指向HTML頁(yè)面中的<html>元素。另一個(gè)就是通過(guò)childNodes列表訪問(wèn)文檔元素桐筏,但通過(guò)documentElement屬性則能更快捷纸型、直接的訪問(wèn)元素。
<html>
<body>
</body>
</html>
這個(gè)頁(yè)面經(jīng)瀏覽器解析后梅忌,其文檔中只包含一個(gè)子節(jié)點(diǎn)狰腌,即<html>
元素∧恋可以通過(guò)documentElement或childNodes列表來(lái)訪問(wèn)這個(gè)元素琼腔。
var html = document.documentElement; // 取得對(duì)<html>的引用
console.log(html === document.childNodes[0]); // true
console.log(html === document.firstChild); // true
documentElement、firstChild和childNodes[0]的值相同蹋笼。展姐,都指向<html>元素。
作為HTMLDocument的實(shí)例剖毯,document對(duì)象還有一個(gè)body屬性圾笨,直接指向<body>元素。因?yàn)殚_(kāi)發(fā)人員經(jīng)常要使用這個(gè)元素逊谋,所以document.body在JS代碼匯總出現(xiàn)的頻率很高擂达。
var body = document.body;
所有瀏覽器都支持document.documentElement和document.body屬性。
Document另一個(gè)可能的子節(jié)點(diǎn)就是DocumentType胶滋。通常將<!DOCTYPE>標(biāo)簽看成一個(gè)與文檔其他部分不同的實(shí)體板鬓,可以通過(guò)doctype屬性來(lái)訪問(wèn)它的信息悲敷。
var doctype = document.doctype;
類似的,還有如下功能方法俭令,取得文檔信息后德。
取得文檔標(biāo)題
var originalTitle = document.title;
設(shè)置文檔標(biāo)題
document.title = "New page ttile";
網(wǎng)頁(yè)請(qǐng)求相關(guān)方法;
- URL:屬性中包含頁(yè)面完整的URL(即地址欄中顯示的URL)
- domain:只包含頁(yè)面的域名
- referrer:保存鏈接到當(dāng)前頁(yè)面的頁(yè)面的URL(從哪到這的)抄腔,referrer屬性可能包含空字符串瓢湃。所有信息都存在請(qǐng)求HTTP頭部,只不過(guò)通過(guò)這些屬性讓我們能夠在JavaScript中訪問(wèn)它們而已赫蛇。
//取得完整URL
var url = document.URL;
//取得域名
var domain = document.domain;
//取得來(lái)源頁(yè)面的URL
var referrer = document.referrer;
URL和domain屬性是相互關(guān)聯(lián)的绵患。例如,如果document.URL為http://www.wrox.com/WileyCDA/
悟耘,那么document.domain就是www.wrox.com
落蝙。
這三個(gè)屬性中,只有domain是可以設(shè)置的暂幼,但由于安全方面限制筏勒,并非可以給domain設(shè)置任何值。如果URL包含一個(gè)子域名粟誓,例如p2p.wrox.com奏寨,那么就只能將domain設(shè)置為wrox.com
。不能將這個(gè)屬性設(shè)置為URL中不包含的域鹰服。
// 假設(shè)頁(yè)面來(lái)自:p2p.wrox.com
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出粗!
當(dāng)頁(yè)面中包含來(lái)自其他子域的框架或內(nèi)嵌框架時(shí)揽咕,能夠設(shè)置domain就非常方便了悲酷。由于跨域安全限制,來(lái)自不同子域的頁(yè)面無(wú)法通過(guò)JavaScript通信亲善。而通過(guò)將每個(gè)頁(yè)面的document.domain設(shè)置為相同的值设易,這些頁(yè)面就可以互相訪問(wèn)對(duì)方包含的JavaScript對(duì)象了。例如蛹头,假設(shè)有一個(gè)頁(yè)面加載自www.wrox.com顿肺,其中包含一個(gè)內(nèi)嵌框架,框架內(nèi)的頁(yè)面加載來(lái)自p2p.wrox.com渣蜗。由于document.domain字符串不一樣屠尊,內(nèi)外兩個(gè)頁(yè)面之間無(wú)法相互訪問(wèn)對(duì)方的JavaScript對(duì)象。但如果將兩個(gè)頁(yè)面的document.domain值設(shè)置為wrox.com耕拷,他們之間就可以通信了讼昆。
瀏覽器對(duì)domain屬性還有一個(gè)限制,即如果域名一開(kāi)始是松散的骚烧,那么不能將它設(shè)置為緊繃的浸赫。換句話說(shuō)闰围,在將document.domain設(shè)置為wrox.com之后,不能再將其設(shè)置回p2p.wrox.com既峡,否認(rèn)導(dǎo)致錯(cuò)誤羡榴。
// 假設(shè)頁(yè)面來(lái)自于p2p.wrox.com域
document.domain = "wrox.com"; // 松散的(成功)
document.domain = "p2p.wrox.com" // 緊繃的(出錯(cuò)!)
查找元素
Document類型提供的兩個(gè)方法:
- getElementById()
- getElementsByTagName()
第一個(gè)方法运敢,接受一個(gè)參數(shù):要取得的元素ID炕矮,如果找到相應(yīng)的元素則返回該元素,如果不存在帶有相應(yīng)的ID的元素者冤,則返回null肤视。注意,這里的ID必須與頁(yè)面中的元素的ID特性嚴(yán)格匹配涉枫,包括大小寫(xiě)邢滑。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var div = document.getElementById("myDiv"); // 取得對(duì)<div>元素的引用
</script>
</body>
</html>
但是,在IE7下和更早版本的瀏覽器都將返回null愿汰。
var div = document.getElementById("mydiv");
IE8及較早版本不區(qū)分ID的大小寫(xiě)困后,因此"myDiv"和"mydiv"會(huì)被當(dāng)作相同的元素ID。
如果頁(yè)面中多個(gè)元素的ID值相同衬廷,getElmentById()只返回文檔中第一次出現(xiàn)的元素摇予。
document.getElementsByTagName()
接受一個(gè)參數(shù),既要取得元素的標(biāo)簽名,返回的是包含零或多個(gè)元素的Nodelist芋膘。在HTML文檔中车柠,這個(gè)方法會(huì)返回一個(gè)HTMLCollection對(duì)象,作為一個(gè)動(dòng)態(tài)集合酗宋,該對(duì)象與Nodelist對(duì)象非常類似。例如疆拘,下列代碼回取得頁(yè)面中所有的<img>元素蜕猫,并返回一個(gè)HTMLCollection。
var images = document.getElementsByTagName("img");
這行代碼會(huì)將一個(gè)HTMLCollection對(duì)象保存在images變量中哎迄。與Nodelist對(duì)象類似回右,可以使用方括號(hào)語(yǔ)法或item()方法來(lái)訪問(wèn)對(duì)象中的項(xiàng)。而這個(gè)對(duì)象中元素的數(shù)量則可以通過(guò)其length屬性取得漱挚。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var divs = document.getElementsByTagName("div");
console.log(divs.length); // 1 輸出div的數(shù)量
console.log(divs[0].id); // myDiv 輸出第一個(gè)div元素的ID特性
console.log(divs.item(0).id); // myDiv 輸出第一個(gè)div元素的ID特性
</script>
</body>
</html>
HTMLCollection對(duì)象還有一個(gè)方法翔烁,叫做namedItem(),使用這個(gè)方法可以通過(guò)元素的那么name特性取得集合中的項(xiàng)棱烂。例如租漂,假設(shè)上面提到的頁(yè)面中包含如下<div>元素
<div id="myDiv" name="myDiv">
那么可以通過(guò)如下方式從divs變量中取得這個(gè)<div>元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" name="myDiv"></div>
<script>
var divs = document.getElementsByTagName("div");
var div = divs.namedItem("myDiv");
console.log(div); // div#myDiv
</script>
</body>
</html>
咋提供按索引訪問(wèn)項(xiàng)的基礎(chǔ)上,HTMLCollection還支持按名稱訪問(wèn)項(xiàng)哩治,這就為我們?nèi)〉脤?shí)際想要的元素提供了便利秃踩。而且,對(duì)命名的項(xiàng)也可以使用方括號(hào)語(yǔ)法來(lái)訪問(wèn)业筏。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var divs = document.getElementsByTagName("div");
var div = divs["myDiv"];
console.log(div); // div#myDiv
</script>
</body>
</html>
第三個(gè)方法憔杨,也就是只有HTMLDocument類型才有的方法,是getElementsByName()蒜胖,顧名思義消别,方法會(huì)返回帶有給定name特性的所有元素。最常使用getElementsByName()方法的情況是取得單選按鈕台谢。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<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>
<script>
var radios = document.getElementsByName("color");
</script>
</body>
</html>
所有的單選按鈕的name特性值相同都是color寻狂,但它們的ID不同,ID作用在于將label元素應(yīng)用到每個(gè)單選按鈕朋沮,而name特性則用以確保三個(gè)值中只有一個(gè)被發(fā)送給瀏覽器蛇券。這樣,我們?nèi)〉盟袉芜x按鈕樊拓。
特殊集合
除了屬性和方法纠亚,document對(duì)象還有一些特殊的集合,這些集合都是HTMLCollection對(duì)象筋夏,為訪問(wèn)文檔常用的部分提供了快捷方式蒂胞,包括:
- document.anchors,包含文檔中所有帶name特性的<a>元素
- document.applets条篷,包含文檔中所有的<applet>元素骗随,很少使用。
- document.forms拥娄,包含所有的<form>元素
- document.images蚊锹,包含所有的<img>元素
- document.links,包含文檔中所有的帶href特性的<a>元素稚瘾。
Element類型
訪問(wèn)元素的標(biāo)簽名,使用nodeName屬性姚炕,也可以使用tagName屬性摊欠;這兩個(gè)屬性會(huì)返回相同的值,使用后者主要是為了清晰可見(jiàn)柱宦。
<div id="myDiv"></div>
可以先這樣取得元素及其標(biāo)簽名:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.tagName); // "DIV"
console.log(div.tagName === div.nodeName); // true
</script>
</body>
</html>
所有的HTML元素都由HTMLElement類型表示些椒,不是直接通過(guò)這個(gè)類型,也是通過(guò)它的子類型表示掸刊。HTMLElement類型直接繼承自Element并添加了一些屬性免糕。添加的這些屬性分別對(duì)應(yīng)于每個(gè)HTML元素中都存在的下列標(biāo)準(zhǔn)特性。
- id,元素在文檔中的唯一標(biāo)識(shí)符
- title石窑,元素附加說(shuō)明信息牌芋,通過(guò)工具提示條顯示
- lang,元素語(yǔ)言代碼
- dir松逊,語(yǔ)言方向躺屁,ltr(left to right),rtl(right to left)经宏,很少使用犀暑。
- className,與元素class屬性對(duì)應(yīng)烁兰。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
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
</script>
</body>
</html>
也可以為每個(gè)屬性賦值耐亏,修改對(duì)應(yīng)屬性的特性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
var div = document.getElementById("myDiv");
div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir = "rtl";
console.log(div.id);
console.log(div.className);
console.log(div.title);
console.log(div.lang);
console.log(div.dir);
/*
someOtherId
ft
Some other text
fr
rtl
*/
</script>
</body>
</html>
*取得特性
getAttribute()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.getAttribute("id"));
console.log(div.getAttribute("class"));
console.log(div.getAttribute("title"));
console.log(div.getAttribute("lang"));
console.log(div.getAttribute("dir"));
/*
myDiv
bd
Body text
en
ltr
*/
</script>
</body>
</html>
傳遞給方法的特性名與實(shí)際的特性名相同沪斟,因此要想得到class的特性值广辰,應(yīng)該傳入class而不是className,后者只有在通過(guò)對(duì)象屬性訪問(wèn)特性時(shí)采用币喧。如果給定名稱特性名不存在轨域,則返回null。
當(dāng)然杀餐,你可以添加自定義特性干发,標(biāo)準(zhǔn)HTML中沒(méi)有的特性。
<div id="myDiv" my_special_attrbute="hello!"></div>
這個(gè)元素包含一個(gè)名為my_special_attrbute
的自定義特性史翘,值為hello!
枉长,可以像取得其他特性一樣取得這個(gè)值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" my_special_attrbute="hello!"></div>
<script>
var div = document.getElementById("myDiv");
var value = div.getAttribute("my_special_attrbute");
console.log(value); // hello!
</script>
</body>
</html>
不過(guò)琼讽,只有公認(rèn)的特性才會(huì)以屬性的形式添加到DOM對(duì)象中
<div id="myDiv" align="left" my_special_attribute="hello!"></div>
因?yàn)閕d和align在HTML中是<div>的公認(rèn)特性必峰,因此該元素的DOM對(duì)象中也將存在對(duì)應(yīng)的屬性。不過(guò)钻蹬,自定義特性my_special_attribute
在Safari吼蚁、Opera、Chrome及Firefox是不存在的问欠;但I(xiàn)E卻會(huì)為自定義特性也創(chuàng)建屬性肝匆。
console.log(div.id); // "myDiv"
console.log(div.my_special_attribute); // undefined(IE除外)
console.log(div.align); // "left"
有兩類特殊的特性,它們雖然有對(duì)應(yīng)的屬性名顺献,但屬性值與通過(guò)getAttribute()返回的值并不相同旗国,第一類是style,通過(guò)CSS為元素指定形式注整。通過(guò)getAttribute()訪問(wèn)時(shí)能曾,返回的style特性值中包含的是CSS文本度硝,而通過(guò)屬性來(lái)訪問(wèn)它則返回一個(gè)對(duì)象。由于style屬性用于以編程方式訪問(wèn)元素樣式的寿冕,因此并沒(méi)有映射到style屬性蕊程。
第二類與眾不同的特性時(shí)onclick這樣的事件處理程序,當(dāng)在元素上使用時(shí)蚂斤,onclick特性中包含的是JavaScript代碼存捺,如果通過(guò)getAttribute()訪問(wèn),則會(huì)返回相應(yīng)的代碼的字符串曙蒸。而在訪問(wèn)onclick屬性時(shí)捌治,則會(huì)返回一個(gè)JavaScript函數(shù)(如果未指定,返回null)這是因?yàn)閛nclick及其他事件處理程序?qū)傩员旧砭蛻?yīng)該被賦予函數(shù)值纽窟。
由于這些差別肖油,通過(guò)JavaScript編程方式操作DOM時(shí),開(kāi)發(fā)者經(jīng)常不使用getAttribute()臂港,而是只使用對(duì)象屬性森枪,只有在取得自定義特性值情況下,才會(huì)使用getAttribute()方法审孽。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div id="myDiv" style="background-color:red;width:100px;height:100px"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.getAttribute("style")); // background-color:red;width:100px;height:100px
console.log(div.style);// CSSStyleDeclaration {0: "background-color", 1: "width", 2: "height", alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
</script>
</body>
</html>
設(shè)置特性
與getAttribute()對(duì)應(yīng)的方法是setAttribute()县袱,這個(gè)方法接受兩個(gè)參數(shù):要設(shè)置的特性名和值。如果特性已經(jīng)存在佑力,setAttribute()會(huì)以指定的值替換現(xiàn)有的值式散;如果特性不存在,setAttribute()則創(chuàng)建該屬性并設(shè)置相應(yīng)的值打颤。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div></div>
<script>
var div = document.getElementsByTagName("div")[0];
div.setAttribute("id","someOtherId");
div.setAttribute("class","ft");
div.setAttribute("title","Some other text");
div.setAttribute("lang","fr");
div.setAttribute("dir","rtl");
</script>
</body>
</html>
通過(guò)方法既可以操作HTML特性暴拄,也可以操作自定義特性。通過(guò)這個(gè)方法設(shè)置的特性名會(huì)統(tǒng)一轉(zhuǎn)換為小寫(xiě)形式编饺,即ID最終會(huì)成為id乖篷。
不過(guò),添加自定義屬性透且,該屬性不會(huì)自動(dòng)成為元素的特性撕蔼。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div></div>
<script>
var div = document.getElementsByTagName("div")[0];
div.mycolor = "red";
console.log(div.getAttribute("mycolor")); // null
</script>
</body>
</html>
attributes屬性
Element類型是使用attributes屬性的唯一一個(gè)DOM節(jié)點(diǎn)類型。attributes屬性中包含一個(gè)NamedNodeMap秽誊,與Nodelist類似罕邀,也是一個(gè)動(dòng)態(tài)集合。元素的每一個(gè)特性都由一個(gè)Attr節(jié)點(diǎn)表示养距,每個(gè)節(jié)點(diǎn)都保存在NamedNodeMap對(duì)象中。NamedNodeMap擁有下列方法:
- getNamedItem(name):返回nodeName屬性等于name的節(jié)點(diǎn)日熬;
- removeNamedItem(name):從列表中移除nodeName屬性等于name的節(jié)點(diǎn)棍厌;
- setNamedItem(node):向列表中添加節(jié)點(diǎn),以節(jié)點(diǎn)的nodeName屬性為索引;
- item(pos):返回位于數(shù)字pos位置處的節(jié)點(diǎn)耘纱。
attributes屬性中包含一系列節(jié)點(diǎn)敬肚,每個(gè)節(jié)點(diǎn)的nodeName就是特性的名稱,而節(jié)點(diǎn)的nodeValue就是特性的值束析。要取得元素的id特性艳馒,可以:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div id="myDiv"></div>
<script>
var div = document.getElementById("myDiv");
var id = div.attributes.getNamedItem("id").nodeValue;
console.log(id); // myDiv
</script>
</body>
</html>
或者使用方括號(hào)語(yǔ)法通過(guò)特性名稱訪問(wèn)節(jié)點(diǎn)的簡(jiǎn)寫(xiě)方式。
var id = div.attributes["id"].nodeValue;
也可以通過(guò)以上的方法設(shè)置特性的值员寇。
div.attributes["id"].nodeValue = "someOtherId";
一般來(lái)說(shuō)弄慰,attributes的方法不夠方便,更多的會(huì)使用getAttribute()蝶锋、removeAttribute()和setAttribute()方法陆爽。
不過(guò),如果想要遍歷元素特性扳缕,attributes屬性可以派上用場(chǎng)慌闭。在需要將DOM結(jié)構(gòu)序列化為XML或者HTML字符串時(shí),多數(shù)都會(huì)涉及遍歷元素特性躯舔。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div id="myDiv" class="div_class" align="left" name="DIV"></div>
<script>
var div = document.getElementById("myDiv");
function outputAttributes(element) {
var attrName;
var attrValue;
var pairs = [];
var len = element.attributes.length;
for (var i = 0; i < len; i++) {
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
pairs.push(attrName + '=\"' + attrValue + '\"');
}
return pairs;
}
console.log(outputAttributes(div));
</script>
</body>
</html>
針對(duì)attributes對(duì)象中的特性驴剔,不同瀏覽器返回的順序不同。這些特性在XML和HTML代碼中出現(xiàn)的先后順序粥庄,不一定與它們出現(xiàn)在attributes對(duì)象中的順序一致丧失。
IE7及更早版本會(huì)返回HTML元素中所有可能的特性,包括沒(méi)有指定的特性飒赃。換句話說(shuō)利花,返回100多個(gè)特性的情況會(huì)很常見(jiàn)。
創(chuàng)建元素
var div = document.createElement("div");
使用createElement()方法創(chuàng)建新元素的同時(shí)载佳,也為新元素設(shè)置了ownerDocument屬性炒事。此時(shí),還可以操作元素的特性蔫慧,為它添加更多子節(jié)點(diǎn)挠乳,以及執(zhí)行其他操作。
div.id = "myNewDiv";
div.className = "box";
然后添加到文檔數(shù)中姑躲∷铮可以使用appendChild()、insertBefore()黍析、replaceChild()方法卖怜。
document.body.appendChild(div);