DOM概覽
上圖的每個(gè)方框是文檔的一個(gè)節(jié)點(diǎn),它表示一個(gè)Node對(duì)象窜司。
注意,通用的Document和Element類(lèi)型與HTMLDocument和HTMLElement類(lèi)型之間是有嚴(yán)格的區(qū)別的树肃。Document類(lèi)型代表一個(gè)HTML或XML文檔泽腮,Element類(lèi)型代表該文檔中的一個(gè)元素瞒爬。HTMLDocument和HTMLElement子類(lèi)只是針對(duì)于HTML文檔和元素牛柒。
選擇文檔元素
獲取文檔的一個(gè)或多個(gè)元素有如下方法:
- 用指定的id屬性
- 用指定的name屬性
- 用指定的標(biāo)簽名字
- 用指定的CSS類(lèi)(class屬性)
- 用指定的CSS選擇器
通過(guò)ID選取元素
可以用Document對(duì)象的getElementById()方法選取一個(gè)基于唯一ID的元素务豺,返回包含單個(gè)Element的Node對(duì)象蹦狂。
// 選擇id為"section1"的唯一元素
var section1 = document.getElementById("section1");
通過(guò)name選擇元素
區(qū)別于id承疲,name屬性的值不是必須唯一,多個(gè)元素可以有同樣的名字鸥咖。
getElementsByName()定義在HTMLDocument類(lèi)中燕鸽,而不是在Document類(lèi)中,所以它只針對(duì)HTML文檔可用啼辣,返回包含多個(gè)Elements的NodeList對(duì)象啊研。
注意:對(duì)于<iframe>
元素,返回值不是元素自身的Element對(duì)象鸥拧,而是表示<iframe>
元素創(chuàng)建的嵌套瀏覽器窗體的Window對(duì)象党远。
// 選擇name為"favorite"的所有元素
var radiobuttons = document.getElementsByName("favorite");
通過(guò)標(biāo)簽名選擇元素
Document對(duì)象的getElementsByTagName()方法可用來(lái)選取指定標(biāo)簽的所有HTML或XML元素,返回包含多個(gè)Elements的NodeList對(duì)象富弦。
// 選擇第1個(gè)<p>元素
var firstpara = document.getElementByTagName("p")[0];
由于歷史原因沟娱,HTMLDocument類(lèi)定義了一些快捷屬性來(lái)訪問(wèn)各種各樣的節(jié)點(diǎn):
- images、forms和links屬性指向
<img>
腕柜、<form>
和<a>
的元素集合济似。這些屬性指代HTMLCollection對(duì)象,很像NodeList對(duì)象盏缤。
// 引用id為"shipping_address"的form元素
document.forms.shipping_address;
- head砰蠢、body屬性與上面不同,指向單個(gè)元素而不是元素的集合唉铜。
// 引用body元素
document.body;
通過(guò)CSS類(lèi)(class)選擇元素
類(lèi)似getElementsByTagName()台舱,在HTML文檔和HTML元素上都可以調(diào)用getElementsByClassName(),它的返回值是一個(gè)實(shí)時(shí)的的NodeList對(duì)象潭流,包含文檔或元素所有匹配的后代節(jié)點(diǎn)竞惋。
getElementsByClassName()只需要一個(gè)字符串參數(shù),但是該字符串可以由多個(gè)空格隔開(kāi)的標(biāo)識(shí)符組成灰嫉,只有當(dāng)元素的class屬性值包含所有的標(biāo)識(shí)符時(shí)才匹配拆宛,但是標(biāo)識(shí)符的順序是無(wú)關(guān)緊要的。
// 查看id為"log"的元素的所有后代中熬甫,類(lèi)名中包含"fatal"和"error"的元素集合
var log = document.getElementById("log");
var fatal = log.getElementsByClassName("fatal error");
通過(guò)CSS選擇器選擇元素
- 用ID胰挑、標(biāo)簽名或類(lèi)名選擇
#nav // id="nav"的元素
div // 所有<div>元素
.warning // 所有在class屬性中包含"warning"的元素
- 基于屬性值來(lái)選取
p[lang="fr"] // 所有使用法語(yǔ)的段落,如:<p lang="fr">
*[name="x"] // 所有包含name="x"屬性的元素
- 組合使用
span.fatal.error // class中包含"fatal"和"error"的所有<span>元素
span[lang="fr"].warning // 所有使用法語(yǔ)且class中包含"warning"的<span>元素
- 基于文檔結(jié)構(gòu)選取
#log span // id="log"元素的 *后代元素* 中的所有<span>元素
#log>span // id="log"元素的 *子元素* 中的所有<span>元素
body>h1:first-child // <body>的子元素中的第一個(gè)<h1>元素
Document對(duì)象的querySelectorAll()方法椿肩,接收一個(gè)包含CSS選擇器的字符串參數(shù)瞻颂,返回匹配選擇器的所有元素的NodeList對(duì)象,但是NodeList對(duì)象并不是實(shí)時(shí)的郑象。
同時(shí)贡这,還有個(gè)方法querySelector(),但它只返回第1個(gè)匹配的元素(以文檔順序)或者沒(méi)有匹配就返回null厂榛。
注意:CSS定義了":first-line"和":first-letter"等偽元素盖矫,在CSS中,它們匹配文本節(jié)點(diǎn)的一部分而不是實(shí)際元素击奶。如果和querySelectorAll()和querySelector()一起使用它們是不匹配的辈双。
文檔結(jié)構(gòu)和遍歷
文檔的節(jié)點(diǎn)(Node)樹(shù)
Document對(duì)象、它的Element對(duì)象和文檔中表示文本的Text對(duì)象都是Node對(duì)象柜砾。
Node有以下一些屬性:
屬性 | 意義 |
---|---|
parentNode | 該節(jié)點(diǎn)的父節(jié)點(diǎn)湃望,Document對(duì)象的父節(jié)點(diǎn)是null。 |
childNodes | 該節(jié)點(diǎn)的子節(jié)點(diǎn)的實(shí)時(shí)表示(NodeList對(duì)象)痰驱。 |
firstChild证芭、lastChild | 該節(jié)點(diǎn)的第1個(gè)和最后1個(gè)子節(jié)點(diǎn),如果沒(méi)有子節(jié)點(diǎn)則為null担映。 |
nextSibling废士、previoursSibling | 該節(jié)點(diǎn)的兄弟節(jié)點(diǎn)的下一個(gè)和前一個(gè)。 |
nodeType | 該節(jié)點(diǎn)的類(lèi)型蝇完。1代表Element節(jié)點(diǎn)官硝,3代表Text節(jié)點(diǎn),8代表Comment節(jié)點(diǎn)短蜕,9代表Document節(jié)點(diǎn)泛源,11代表DocumentFragment節(jié)點(diǎn)。 |
nodeValue | Text節(jié)點(diǎn)或Comment節(jié)點(diǎn)的文本內(nèi)容忿危。 |
nodeName | 元素的標(biāo)簽名达箍,以大寫(xiě)形式表示。 |
文檔的元素(Element)樹(shù)
如果將文檔Element對(duì)象樹(shù)铺厨,將忽略Text和Comment節(jié)點(diǎn)缎玫。
Element對(duì)象的有以下屬性:
屬性 | 意義 |
---|---|
parentNode | 該節(jié)點(diǎn)的父節(jié)點(diǎn),任何Element的parentNode總是另一個(gè)Element解滓,或者追溯到樹(shù)根的Document或DocumentFragment節(jié)點(diǎn)赃磨。 |
children | 類(lèi)似ChildNodes,它也是一個(gè)NodeList對(duì)象洼裤,但不同的是children列表只包含Element對(duì)象邻辉。 |
firstElementChild, lastElementChild | 類(lèi)似firstChild和lastChild,但只代表子Element。 |
nextElementSibling, previousElementSibling | 類(lèi)似nextSibling和previousSibling值骇,但只代表兄弟Element莹菱。 |
childElementCount | 子元素的數(shù)量。返回的值和children.length值相等吱瘩。 |
示例代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- 注釋 -->
<div class="fox" id="box">123</div>
<script>
//【1】元素節(jié)點(diǎn)
var nodeElement = document.body;
console.log(nodeElement.nodeName, nodeElement.nodeValue, nodeElement.nodeType, nodeElement.nodeType==Node.ELEMENT_NODE);//BODY null 1 true
//【2】元素特性在DOM中以Attr類(lèi)型表示道伟,是存在于元素的attributes屬性中的節(jié)點(diǎn),但卻不是DOM文檔樹(shù)的一部分使碾。
var nodeAttribute = document.getElementById("box").attributes[0] ;
console.log(nodeAttribute.nodeName, nodeAttribute.nodeValue, nodeAttribute.nodeType,nodeAttribute.nodeType == Node.ATTRIBUTE_NODE)//id box 2 true (示例中包含2個(gè)屬性蜜徽,如果是attributes[1]是輸出"class fox 2 true")
//【1】元素節(jié)點(diǎn)
var nodeChildElement = document.body.firstElementChild;
console.log(nodeChildElement.nodeName, nodeChildElement.nodeValue, nodeChildElement.nodeType,nodeChildElement.nodeType == Node.ELEMENT_NODE)//DIV null 1 true
//【3】文本節(jié)點(diǎn)
var nodeText = document.body.firstElementChild.firstChild;
console.log(nodeText.nodeName, nodeText.nodeValue, nodeText.nodeType,nodeText.nodeType == Node.TEXT_NODE)//#text 123 3 true
//【4】CDATASection類(lèi)型只針對(duì)基于XML的文檔,只出現(xiàn)在XML文檔中票摇,表示的是CDATA區(qū)域
//【5】ENTITY_REFERENCE_NODE 實(shí)體引用名稱(chēng)節(jié)點(diǎn)
//【6】ENTITY_NODE 實(shí)體名稱(chēng)節(jié)點(diǎn)
//【7】PROCESSING_INSTRUCTION_NODE 處理指令節(jié)點(diǎn)
//【8】注釋節(jié)點(diǎn)
var nodeComment = document.body.childNodes[1];
console.log(nodeComment.nodeName, nodeComment.nodeValue, nodeComment.nodeType,nodeComment.nodeType == Node.COMMENT_NODE)//#comment 注釋 8 true
//【9】文檔節(jié)點(diǎn)
var nodeDocument = document;
console.log(nodeDocument.nodeName, nodeDocument.nodeValue, nodeDocument.nodeType,nodeDocument.nodeType==Node.DOCUMENT_NODE);//#document null 9 true
//【10】文檔類(lèi)型節(jié)點(diǎn)
var nodeDocumentType = document.firstChild;
console.log(nodeDocumentType.nodeName, nodeDocumentType.nodeValue, nodeDocumentType.nodeType,nodeDocumentType.nodeType==Node.DOCUMENT_TYPE_NODE);//html null 10 true
//【11】DocumentFragment文檔片段類(lèi)型在文檔中沒(méi)有對(duì)應(yīng)的標(biāo)記拘鞋,是一種輕量級(jí)的文檔。
var nodeDocumentFragment = document.createDocumentFragment();
console.log(nodeDocumentFragment.nodeName, nodeDocumentFragment.nodeValue, nodeDocumentFragment.nodeType,nodeDocumentFragment.nodeType == Node.DOCUMENT_FRAGMENT_NODE)//#document-fragment null 11 true
//【12】NOTATION_NODE DTD中聲明的符號(hào)
</script>
</body>
</html>
元素屬性
HTML元素由一個(gè)標(biāo)簽和一組稱(chēng)為屬性(attribute)的名/值對(duì)組成矢门。
HTML標(biāo)準(zhǔn)屬性
HTMLElement定義了通用的HTTP屬性掐禁,如id、lang颅和、dir傅事,以及事件處理程序,如onclick峡扩。
標(biāo)準(zhǔn)屬性有以下特點(diǎn):
- HTML屬性名不區(qū)分大小寫(xiě)蹭越,但JavaScript屬性名則對(duì)大小寫(xiě)敏感。從HTML屬性名轉(zhuǎn)換到JavaScript屬性名時(shí)應(yīng)該采用小寫(xiě)教届,如果包含多個(gè)單詞响鹃,則除第一個(gè)以外的單詞的首字母大寫(xiě),如:defatultCheked.
- 有些HTML屬性在JavaScript中是保留字案训。對(duì)于這些屬性买置,一般的規(guī)則是為屬性名加前綴"html"。如for屬性在JavaScript中變?yōu)閔tmlFor强霎;但class屬性是一個(gè)例外忿项,在JavaScript中它為className。
- 表示HTML屬性的值通常是字符串城舞,但也有布爾值或數(shù)值的屬性轩触,如defaultChecked和maxLength。事件處理程序的屬性則是Function對(duì)象(或null)家夺。HTML元素的style屬性值是CSSStyleDeclaration對(duì)象脱柱。
HTML非標(biāo)準(zhǔn)屬性
Element類(lèi)型定義了getAttribute()和setAttribute()方法來(lái)查詢(xún)和設(shè)置非標(biāo)準(zhǔn)的HTML屬性,也可用于查詢(xún)和設(shè)置XML文檔的屬性拉馋。
非標(biāo)準(zhǔn)屬性有以下特點(diǎn):
- 屬性值都被看做是字符串榨为。
- 方法使用標(biāo)準(zhǔn)屬性名惨好,甚至當(dāng)這些名稱(chēng)是JavaScript保留字時(shí)也不例外。
var image = document.images[0];
var width = parseInt(image.getAttribute("WIDTH")); // 需要調(diào)用parseInt()將字符串轉(zhuǎn)換成int
image.setAttribute("class", "thumbnail"); // "class"屬性
數(shù)據(jù)集屬性(dataset)
有時(shí)候我們需要在HTML元素上綁定一些額外的信息随闺,可以使用getAttribute()和setAttribute()來(lái)讀和寫(xiě)非標(biāo)準(zhǔn)屬性的值日川,但為此付出的代價(jià)是文檔將不再是合法有效的HTML。
HTML5提供了一個(gè)解決方案板壮。在HTML5文檔中逗鸣,任意以"data-"為前綴的小寫(xiě)的屬性名字都是合法的合住。
HTML5還在Element對(duì)象上定義了dataset屬性绰精。該屬性指代一個(gè)對(duì)象,它的各個(gè)屬性對(duì)應(yīng)于去掉前綴的data-屬性透葛。帶連字符的屬性對(duì)應(yīng)于駝峰命名法屬性名:data-jquery-test屬性就變成dataset.jqueryTest屬性笨使。
attributes屬性
Node類(lèi)型定義了attributes屬性。針對(duì)非Element對(duì)象的節(jié)點(diǎn)僚害,該屬性為null硫椰。對(duì)于Element對(duì)象,attributes屬性是實(shí)時(shí)只讀的類(lèi)數(shù)組對(duì)象萨蚕,它代表元素的所有屬性靶草。Attr對(duì)象是一類(lèi)特殊的Node.
document.body.attributes[0]; // <body>元素的第1個(gè)屬性
document.body.attributes.bgcolor; // <body>元素的bgcolor屬性
document.body.attributes["ONLOAD"]; // <body>元素的onload屬性
元素內(nèi)容
innerHTML&outerHTML屬性
- innerHTML屬性返回元素的內(nèi)容(可能包含其他element元素)。
- outerHTML屬性返回元素的標(biāo)簽與內(nèi)容岳遥。
如奕翔,對(duì)于<p>
元素:<p>This is a <i>simple</i> document</p>
innerHTML屬性的值為:This is a <i>simple</i> document
outerHTML屬性的值為:<p>This is a <i>simple</i> document</p>
textContent屬性
有時(shí)需要查詢(xún)純文本形式的內(nèi)容,或在文檔中插入純文本浩蓉,則可以使用Node的textContent屬性來(lái)實(shí)現(xiàn)派继。
textContent屬性就是將指定元素的所有后代Text節(jié)點(diǎn)簡(jiǎn)單地串聯(lián)在一起。
如捻艳,對(duì)于<p>
元素:<p>This is a <i>simple</i> document</p>
textContent屬性的值為:This is a simple document
注意:在IE中要使用innerText屬性來(lái)代替驾窟。
// 實(shí)現(xiàn)textContent
function textContent(e) {
var child, type, s = "";
for(child = e.firstChild; child != null; child = child.nextSibling) {
type = child.nodeType;
if(type === 3 || type === 4) // Text和CDATASection節(jié)點(diǎn)
s += child.nodeValue;
else if(type === 1) // 遞歸Element節(jié)點(diǎn)
s += textContent(child);
}
return s;
}
創(chuàng)建、插入和刪除節(jié)點(diǎn)
創(chuàng)建節(jié)點(diǎn)
- 創(chuàng)建新的Element節(jié)點(diǎn)可以使用document對(duì)象的createElement()方法认轨。
// 從指定的URL绅络,異步加載和執(zhí)行腳本
function loadasync(url) {
var head = document.getElementsByTagName("head")[0];
var s = document.createElement("script");
s.src = url;
head.appendChild(s);
}
- 還可通過(guò)cloneNode()方法來(lái)創(chuàng)建一個(gè)節(jié)點(diǎn),新創(chuàng)建的節(jié)點(diǎn)以現(xiàn)有的節(jié)點(diǎn)為模板:
<!DOCTYPE html>
<html>
<script>
function myFunction()
{
var itm=document.getElementById("myList2");
var cln=itm.cloneNode(true); // 深拷貝
document.body.appendChild(cln);
}
</script>
<body>
<ul id="myList1"><li>Coffee</li><li>Tea</li></ul>
<ul id="myList2"><li>Water</li><li>Milk</li></ul>
<button onclick="myFunction()">添加一個(gè)列表</button>
</body>
</html>
注意:拷貝出來(lái)的元素id與原始的相同嘁字,但通過(guò)getElementById()獲取到的將還是原始的元素昨稼,而不是拷貝出來(lái)的元素。
插入節(jié)點(diǎn)
Node的方法appendChild()或insertBefore()方法可以實(shí)現(xiàn)將一個(gè)節(jié)點(diǎn)插入到已知文檔中拳锚。
appendChild()將新節(jié)點(diǎn)插入到最后假栓,作為該節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。
insertBefore()則將新節(jié)點(diǎn)插入到指定的子節(jié)點(diǎn)之前霍掺。
注意:如果使用上述方法將已存在的一個(gè)節(jié)點(diǎn)再次插入匾荆,那么節(jié)點(diǎn)將自動(dòng)從它原有的位置刪除并在新的位置重新插入(類(lèi)似于先刪除后新增)拌蜘。
刪除和替換節(jié)點(diǎn)
- removeChild()方法可以從文檔樹(shù)中刪除一個(gè)節(jié)點(diǎn)。
node.parentNode.removeChild(node); // 刪除自身節(jié)點(diǎn)
- replaceChild()方法可以用一個(gè)新節(jié)點(diǎn)替換已存在的節(jié)點(diǎn)牙丽。
node.parentNode.replaceChild(document.createTextNode("[ REPLACED ]"), node); // 替換自身節(jié)點(diǎn)
// 使用innerHTML實(shí)現(xiàn)outerHTML屬性
( function() {
// 如果outerHTML存在简卧,則直接返回
if(document.createElement("div").outerHTML) return;
// get方法
function outerHTMLGetter() {
var container = document.createElement("div"); // 創(chuàng)建一個(gè)虛擬節(jié)點(diǎn)
container.appendChild(this.cloneNode(true));
return container.innerHTML;
}
// set方法
function outerHTMLSetter(value) {
var container = document.createElement("div"); // 創(chuàng)建一個(gè)虛擬節(jié)點(diǎn)
container.innerHTML = value;
// 將value的所有節(jié)點(diǎn)插入到this節(jié)點(diǎn)之前
// NOTE: 由于同一個(gè)documnet中,firstChild插入后烤芦,原始位置上的節(jié)點(diǎn)將被刪除
while(container.firstChild)
this.parentNode.insertBefore(container.firstChild, this);
// 刪除當(dāng)前節(jié)點(diǎn)
this.parentNode.removeChild(this);
}
// 設(shè)置outerHTML屬性的getter和setter
if(Object.defineProperty) {
Object.defineProperty(Element.prototype, "outerHTML", {
get: outerHTMLGetter,
set: outerHTMLGetter,
enumerable: false,
configurable: true
});
} else {
Element.prototype._defineGetter_("outerHTML", outerHTMLGetter);
Element.prototype._defineSetter_("outerHTML", outerHTMLSetter);
}
}());
DocumentFragment節(jié)點(diǎn)
- DocumentFragment是一個(gè)特殊的Node举娩,它作為其他節(jié)點(diǎn)的一個(gè)臨時(shí)的容器。
- 像Document節(jié)點(diǎn)一樣构罗,DocumentFragment節(jié)點(diǎn)是獨(dú)立的铜涉,而不是任何其他文檔的一部分,它的parentNode總是null遂唧。
- 類(lèi)似Element節(jié)點(diǎn)芙代,它可以有任意多的子節(jié)點(diǎn),可以使用appendChild()盖彭、insertBefore()等方法來(lái)操作它們纹烹。
- 將DocumnetFragment節(jié)點(diǎn)插入到文檔中,其實(shí)是將DocumentFragment的所有子節(jié)點(diǎn)插入到文檔召边,而不是DocumentFragment本身铺呵。
// 倒序排列節(jié)點(diǎn)node的子節(jié)點(diǎn)
function reverse(node) {
var f = document.createDocumentFragment();
// NOTE: 給f添加一個(gè)節(jié)點(diǎn),該節(jié)點(diǎn)會(huì)自動(dòng)從node中刪除
while(node.lastChild) f.appendChild(node.lastChild);
// 將臨時(shí)節(jié)點(diǎn)f的所有子節(jié)點(diǎn)全部移回到node中
node.appendChild(f);
}