0. DOM簡介
任何一種技術都是有它存在的意義,DOM正是爲了方便操作HTML文檔而誕生的技術。在沒有DOM的時代,爲了操作HTML元素曾發展出了DHTML(動態HTML),後來由於微軟和Netscape的瀏覽器大戰而銷聲匿跡。DOM脫胎於DHTML,並於1998年DOM 1級成爲了W3C的推薦標準。現在利用JavaScript操作DOM已經成爲基礎前端技術,被各大瀏覽器完善支持。
DOM(Document Object Model,文檔對象模型),是一種針對HTML和XML文檔編程API。DOM將整個HTML或XML文檔映射爲一個層次化的節點樹,用以節點的增刪改查操作,以達到改變頁面的目的。每個HTML或XML元素都可以看成一個節點,每個節點都有對應的數據和層次結構,使用JavaScript等腳本語言可以對節點的進行操作。
例如如下HTML文檔:
<html>
<head>
<title>learnDOM</title>
</head>
<body>
<p>hello</p>
<h1>world</h1>
</body>
</html>
可將此HTML文檔表示爲節點的層次結構:
其實html上冊還有一個根節點:文檔節點。html節點稱爲文檔元素,任何一個HTML文檔只有一個文檔元素,因此html節點能表示整個文檔。整個DOM樹是一種典型的樹狀數據結構,例如body節點擁有兩個子節點p和h1,文本節點"learnDOM"父節點爲title。
1. 節點類型
每個節點都有一個nodeType屬性來表明節點類型。Node類型定義了12個類型,由數值常量1~12來表示。常用節點類型有:
節點類型 | 數值常量 | 字符常量 |
---|---|---|
Element(元素節點) | 1 | ELEMENT_NODE |
Attr(屬性節點) | 2 | ATTRIBUTE_NODE |
Text(文本節點) | 3 | TEXT_NODE |
Comment(註釋節點) | 8 | COMMENT_NODE |
Document(文檔節點) | 9 | DOCUMENT_NODE |
DocumentType(文檔類型節點) | 10 | DOCUMENT_TYPE_NODE |
DocumentFragment(文檔片段節點) | 11 | DOCUMENT_FRAGMENT_NODE |
1)Element(元素節點),是組成節點樹的重要部分,通常擁有子元素、文本節點等子節點,也是唯一能擁有屬性的節點類型,例如:
都是元素節點。
2)Attr(屬性節點),是元素節點的一部分,不能單獨作爲節點出現在節點樹中。例如:
<div id="addr"></div>
中id就是元素節點div的屬性節點。
3)Text(文本節點),只包含文本內容的節點,在XML中也稱爲字符數據,可爲空字符。節點樹中,元素的文本內容和屬性的文本內容都是由文本節點來表示的。例如:
中"learnDOM"就是文本節點。
4)Comment(註釋節點),表示文檔中註釋的內容。
5)Document(文檔節點),是文檔的根節點。在HTML文檔的節點樹中處於html節點之上的節點爲文檔節點。
6)DocumentType(文檔類型節點),每個文檔都有一個類型屬性,值或爲空,或爲DocumentType對象。在HTML中:
<!DOCTYPE html>
就是文檔類型節點。
7)DocumentFragment(文檔片段節點),是文檔的片段,並不屬於文檔節點樹。例如:
var frag = document.createDocumentFragment();
frag就爲一個文檔片段,可對其操作後寫入文檔樹,文檔片段就起了類似臨時變量的作用。
接下來舉一個判斷節點類型的例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>learnDOM</title>
</head>
<body>
<div id="addr">元素節點</div>
<script>
var divNode = document.getElementById("addr");
if (divNode.nodeType === Node.ELEMENT_NODE) {
console.log("A element node");
}
</script>
</body>
</html>
將div節點的id"addr"傳入getElementById()方法,得到一個節點元素divNode。判斷divNode的類型可通過上述if語句。注意IE中只支持與數值常量,ELEMENT_NODE這種字符常量是無法識別的。上例中通過節點元素的nodeType屬性判斷節點類型,除此之外常用屬性還有nodeName和nodeValue。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>learnDOM</title>
</head>
<body>
<!-- learnNodeType -->
<div id="addr">元素節點</div>
<script>
//元素節點
var divNode = document.getElementById("addr");
console.log(divNode.nodeName); //div
console.log(divNode.nodeValue); //null
//屬性節點
var attrNode = divNode.attributes[0];
console.log(attrNode.nodeName); //id
console.log(attrNode.nodeValue); //addr
//文本節點
var textNode = divNode.childNodes[0];
console.log(textNode.nodeName); //#text
console.log(textNode.nodeValue);//元素節點
//註釋節點
var commentNode = document.body.childNodes[1];//註釋前空白爲[0]
console.log(commentNode.nodeName);//#comment
console.log(commentNode.nodeValue);//learnNodeType
//文檔類型節點
console.log(document.doctype.nodeName); //html
console.log(document.doctype.nodeValue);//null
//文檔片段節點
var frag = document.createDocumentFragment();//創建文檔片段
console.log(frag.nodeName); //#document-fragment
console.log(frag.nodeValue); //null
</script>
</body>
</html>
2. DOM方法和屬性
- getElementById(),返回指定ID的元素
- getElementsByTagName(),返回指定標籤名稱的所有元素節點集合
- getElementsByClassName(),返回指定類名的所有元素節點集合
- appendChild(),添加新節點到指定節點
- removeChild(),刪除子節點
- replaceChild(),替換子節點
- insertBefore(),在指定節點前插入新節點
- createAttribute(),創建屬性節點
- createElement(),創建元素節點
- createTextNode(),創建文本節點
- getAttribute(),返回指定屬性值
- setAttribute(),修改指定屬性值
- innerHTML,節點文本值
- parentNode,節點的父節點
- childNodes,節點的子節點
- attributes,節點的屬性節點
2.1 DOM獲取
<h1 id="country">countries</h1>
<div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
<p class="AS">KR</p>
</div>
<script>
//獲取ID爲"country"的節點
var countries = document.getElementById("country");
//先定位ID爲"div_list"的節點,後獲取其內部所有p節點
var ps = document.getElementById("div_list").getElementsByTagName("p");
//先定位ID爲"div_list"的節點,後獲取其內部所有class爲EU的節點
var eus = document.getElementById("div_list").getElementsByClassName("EU");
//獲取div_list所有子節點,第一個子節點,最後一個子節點
var divTest = document.getElementById("div_list");
var divTestChilds = divTest.childNodes;
var firstChild = divTest.firstChild;
var lastChild = divTest.lastChild;
</script>
或者用querySelector()和querySelectorAll(),注意IE8+版本纔有支持。具體用法見https://developer.mozilla.org/zh-CN/docs/Web/API/Document/2.2 DOM修改
//獲取節點
var h = document.getElementById("country");
//設置文本
h.innerHTML = "hello";
//設置HTML
h.innerHTML = "hello <span style=\"color:red\">red</span> ";
var divTest = document.getElementById("div_list");
divTest.style.color = "#00ff00";
divTest.style.fontSize = "10px";
2.3 DOM刪除
//獲取待刪除節點
var self = document.getElementById("div_list");
//獲取父節點
var parent = self.parentNode;
//刪除
var removed = parent.removeChild(self);
console.log(removed === self); //true
若使用父節點的children屬性進行遍歷刪除,注意children屬性是隻讀的,並且隨着操作會實時更新: <div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
<p class="AS">KR</p>
</div>
<script>
var parent = document.getElementById("div_list");
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[5]);//error,節點只剩了五個,children[5]不存在
</script>
2.4 DOM插入
<div id="blank"></div>
<script>
var d = document.getElementById("blank");
d.innerHTML = "hello";
</script>
但是節點非空時會將原內容覆蓋。 <p id="kr">KR</p>
<div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
</div>
<script>
var list = document.getElementById("div_list");
var kr = document.getElementById("kr");
list.appendChild(kr);
</script>
<p id="kr">KR</p>被插入到list最後,並且原位置的kr會被刪除,相當於完成了節點的移動。我們也能從零創建一個新節點並插入父節點中:
var list = document.getElementById("div_list");
var newNode = document.createElement("p");
newNode.id = "kr";
newNode.innerHTML = "KR";
list.appendChild(newNode);
<p id="kr">KR</p>
<div id="div_list">
<p class="AS">CN</p>
<p class="NA">US</p>
<p class="EU">UK</p>
<p class="EU">FR</p>
<p class="AS">JP</p>
</div>
<script>
var list = document.getElementById("div_list");
var ref = list.children[2];
var node = document.getElementById("kr");
list.insertBefore(node, ref);
</script>