javascript中的dom應用實例

DOM
2006-07-31 11:15

文檔對象模型(DOM)是表示文檔(比如HTML和XML)和訪問、操作構成文檔的各種元素的應用程序接口(API)。一般的,支持Javascript的所有瀏覽器都支持DOM。本文所涉及的DOM,是指W3C定義的標準的文檔對象模型,它以樹形結構表示HTML和XML文檔,定義了遍歷這個樹和檢查、修改樹的節點的方法和屬性。

7.4.1、DOM眼中的HTML文檔:樹
  在DOM眼中,HTML跟XML一樣是一種樹形結構的文檔,<html>是根(root)節點,<head>、<title>、<body>是<html>的子(children)節點,互相之間是兄弟(sibling)節點;<body>下面纔是子節點<table>、<span>、<p>等等。如下圖:

  

  這個是不是跟XML的結構有點相似呢。不同的是,HTML文檔的樹形主要包含表示元素、標記的節點和表示文本串的節點。

7.4.2、HTML文檔的節點
  DOM下,HTML文檔各個節點被視爲各種類型的Node對象。每個Node對象都有自己的屬性和方法,利用這些屬性和方法可以遍歷整個文檔樹。由於HTML文檔的複雜性,DOM定義了nodeType來表示節點的類型。這裏列出Node常用的幾種節點類型:
接口 nodeType常量 nodeType值 備註
Element Node.ELEMENT_NODE 1 元素節點
Text Node.TEXT_NODE 3 文本節點
Document Node.DOCUMENT_NODE 9 document
Comment Node.COMMENT_NODE 8 註釋的文本
DocumentFragment Node.DOCUMENT_FRAGMENT_NODE 11 document片斷
Attr Node.ATTRIBUTE_NODE 2 節點屬性

  DOM樹的根節點是個Document對象,該對象的documentElement屬性引用表示文檔根元素的Element對象(對於HTML文檔,這個就是<html>標記)。Javascript操作HTML文檔的時候,document即指向整個文檔,<body>、<table>等節點類型即爲Element。Comment類型的節點則是指文檔的註釋。具體節點類型的含義,請參考《Javascript權威指南》,在此不贅述。

  Document定義的方法大多數是生產型方法,主要用於創建可以插入文檔中的各種類型的節點。常用的Document方法有:

方法 描述
createAttribute() 用指定的名字創建新的Attr節點。
createComment() 用指定的字符串創建新的Comment節點。
createElement() 用指定的標記名創建新的Element節點。
createTextNode() 用指定的文本創建新的TextNode節點。
getElementById() 返回文檔中具有指定id屬性的Element節點。
getElementsByTagName() 返回文檔中具有指定標記名的所有Element節點。

  對於Element節點,可以通過調用getAttribute()、setAttribute()、removeAttribute()方法來查詢、設置或者刪除一個Element節點的性質,比如<table>標記的border屬性。下面列出Element常用的屬性:

屬性 描述
tagName 元素的標記名稱,比如<p>元素爲P。HTML文檔返回的tabName均爲大寫。

  Element常用的方法:

方法 描述
getAttribute() 以字符串形式返回指定屬性的值。
getAttributeNode() 以Attr節點的形式返回指定屬性的值。
getElementsByTabName() 返回一個Node數組,包含具有指定標記名的所有Element節點的子孫節點,其順序爲在文檔中出現的順序。
hasAttribute() 如果該元素具有指定名字的屬性,則返回true。
removeAttribute() 從元素中刪除指定的屬性。
removeAttributeNode() 從元素的屬性列表中刪除指定的Attr節點。
setAttribute() 把指定的屬性設置爲指定的字符串值,如果該屬性不存在則添加一個新屬性。
setAttributeNode() 把指定的Attr節點添加到該元素的屬性列表中。

  Attr對象代表文檔元素的屬性,有name、value等屬性,可以通過Node接口的attributes屬性或者調用Element接口的getAttributeNode()方法來獲取。不過,在大多數情況下,使用Element元素屬性的最簡單方法是getAttribute()和setAttribute()兩個方法,而不是Attr對象。

7.4.3、使用DOM操作HTML文檔
  Node對象定義了一系列屬性和方法,來方便遍歷整個文檔。用parentNode屬性和childNodes[]數組可以在文檔樹中上下移動;通過遍歷childNodes[]數組或者使用firstChild和nextSibling屬性進行循環操作,也可以使用lastChild和previousSibling進行逆向循環操作,也可以枚舉指定節點的子節點。而調用appendChild()、insertBefore()、removeChild()、replaceChild()方法可以改變一個節點的子節點從而改變文檔樹。

  需要指出的是,childNodes[]的值實際上是一個NodeList對象。因此,可以通過遍歷childNodes[]數組的每個元素,來枚舉一個給定節點的所有子節點;通過遞歸,可以枚舉樹中的所有節點。下表列出了Node對象的一些常用屬性和方法:

  Node對象常用屬性:

屬性 描述
attributes 如果該節點是一個Element,則以NamedNodeMap形式返回該元素的屬性。
childNodes 以Node[]的形式存放當前節點的子節點。如果沒有子節點,則返回空數組。
firstChild 以Node的形式返回當前節點的第一個子節點。如果沒有子節點,則爲null。
lastChild 以Node的形式返回當前節點的最後一個子節點。如果沒有子節點,則爲null。
nextSibling 以Node的形式返回當前節點的兄弟下一個節點。如果沒有這樣的節點,則返回null。
nodeName 節點的名字,Element節點則代表Element的標記名稱。
nodeType 代表節點的類型。
parentNode 以Node的形式返回當前節點的父節點。如果沒有父節點,則爲null。
previousSibling 以Node的形式返回緊挨當前節點、位於它之前的兄弟節點。如果沒有這樣的節點,則返回null。

  Node對象常用方法:

方法 描述
appendChild() 通過把一個節點增加到當前節點的childNodes[]組,給文檔樹增加節點。
cloneNode() 複製當前節點,或者複製當前節點以及它的所有子孫節點。
hasChildNodes() 如果當前節點擁有子節點,則將返回true。
insertBefore() 給文檔樹插入一個節點,位置在當前節點的指定子節點之前。如果該節點已經存在,則刪除之再插入到它的位置。
removeChild() 從文檔樹中刪除並返回指定的子節點。
replaceChild() 從文檔樹中刪除並返回指定的子節點,用另一個節點替換它。

  接下來,讓我們使用上述的DOM應用編程接口,來試着操作HTML文檔。

  A、遍歷文檔的節點

  DOM把一個HTML文檔視爲樹,因此,遍歷整個樹是應該是家常便飯。跟之前說過的一樣,這裏我們提供兩個遍歷樹的例子。通過它,我們能夠學會如何使用childNodes[]和firstChile、lastChild、nextSibling、previousSibling遍歷整棵樹。

  例子1-- sample3_1.htm:
  這個例子使用了childNodes[]和遞歸方式來遍歷整個文檔,統計文檔中出現的Element元素總數,並把Element標記名全部打印出來。需要特別注意的是,在使用DOM時,必須等文檔被裝載完畢再執行遍歷等行爲操作文檔。sample3_1.htm具體代碼如下:


  例子2 – sample3_2.htm:
  接下來使用firstChile、lastChild、nextSibling、previousSibling遍歷整個文檔樹。修改一下countTotalElement函數,其他跟sample3_1.htm一樣:


  B、搜索文檔中特定的元素
  在使用DOM的過程中,有時候需要定位到文檔中的某個特定節點,或者具有特定類型的節點列表。這種情況下,可以調用Document對象的getElementsByTagName()和getElementById()方法來實現。

  document.getElementsByTagName()返回文檔中具有指定標記名的全部Element節點數組(也是NodeList類型)。Element出現在數組中的順序就是他們在文檔中出現的順序。傳遞給getElementsByTagName()的參數忽略大小寫。比如,想定位到第一個<table>標記,可以這樣寫:document.getElementsByTagName(“table”)[0]。例外的,可以使用document.body定位到<body>標記,因爲它是唯一的。

  getElementsByTagName()返回的數組取決於文檔。一旦文檔改變,返回結果也立即改變。相比,getElementById()則比較靈活,可以隨時定位到目標,只是要實現給目標元素一個唯一的id屬性值。這個我們在《AJAX開發簡略》的“級聯菜單”例子中已經使用過了。

  Element對象也支持getElementsByTagName()和getElementById()。不同的是,搜索領域只針對調用者的子節點。

  C、修改文檔內容
  遍歷整棵文檔樹、搜索特定的節點,我們最終目的之一是要修改文檔內容。接下來的三個例子將使用Node的幾個常用方法,來演示如何修改文檔內容。

  例子3 -- sample4_1.htm:
  這個例子包含三個文本節點和一個按鈕。點擊按鈕後,三個文本節點和按鈕的順序將被顛倒。程序使用了Node的appendChild()和removeChild()方法。

第一行

第二行

第三行


  例子4-- sample4_2.htm:
  例子1通過直接操作body的子節點來修改文檔。在HTML文檔中,佈局和定位常常通過表格<table>來實現。因此,例子4將演示操作表格內容,將表格的四個單元行順序顛倒。如果沒有使用<tbody>標籤,則<table>把全部的<tr>當做是屬於一個子節點<tbody>,所以我們採用數組緩存的方式,把行數據顛倒一下。這個例子同時也演示瞭如何使用DOM創建表格單元行。

第一行
第二行
第三行
第四行


  例子5 -- sample4_3.htm:
  正如我們在Node節點介紹部分所指出的那樣,appendChild()、replaceChild()、removeChild()、insertBefore()方法會立即改變文檔的結構。下面的例子包含兩個表格,我們試着把表格二的內容替換表格一的內容。

表格一

表格二


  注意,當執行kid1.replaceChild(repKid,kid1.firstChild);的時候,table2的子節點已經被轉移到table1了,table2已經沒有子節點,不能再調用table2的子節點。看看代碼的註釋,試着運行一下,應該就知道文檔是怎麼改變的了。

  D、往文檔添加新內容
  在學會遍歷、搜索、修改文檔之後,我們現在試着網文檔添加新的內容。其實沒有什麼新意,只是利用我們上述提到的Node的屬性和方法而已,還是操作<table>標記的內容。有新意的是,我們要實現一個留言簿。是的,留言簿,你可以往裏面留言,只是不能刷新噢。

  例子6 – sample5_1.htm:

網友留言列表:

  我們之前說過,<table>的子節點是<tbody>,<tbody>的子節點纔是<tr>,<tr>是<td>的父節點,最後<td>內部的TextNode節點。所以,往<table>增加單元格行要逐級形成,就像往樹裏面添加一個枝椏一樣,要有葉子有徑。看看,這個留言簿是不是很簡單啊。這個例子同時也演示了往<table>表格標記裏面增加內容的另一種方法。


  E、使用DOM操作XML文檔
  在數據表示方面,XML文檔更加結構化。DOM在支持HTML的基礎上提供了一系列的API,支持針對XML的訪問和操作。利用這些API,我們可以從XML中提取信息,動態的創建這些信息的HTML呈現文檔。處理XML文檔,通常遵循“加載XML文檔à提取信息à加工信息à創建HTML文檔”的過程。下面的例子演示瞭如何加載並處理XML文檔。

  這個例子包含兩個JS函數。loadXML()負責加載XML文檔,其中既包含加載XML文檔的2級DOM代碼,又有實現同樣操作的Microsoft專有API代碼。需要提醒注意的是,文檔加載過程不是瞬間完成的,所以對loadXML()的調用將在加載文檔完成之前返回。因此,需要傳遞給loadXML()一個引用,以便文檔加載完成後調用。

  例子中的另外一個函數makeTable(),則在XML文檔加載完畢之後,使用最後前介紹過的DOM應用編程接口讀取XML文檔信息,並利用這些信息形成一個新的table表格。

  例子7 -- sample6_1.htm:


 
部分源代碼
一 遍歷文檔節點總數

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>統計Element節點總數</title>
<script language="javascript">
<!--
var elementName = "";
function countTotalElement(node){
var total = 0;
if(node.nodeType == 1){
total ++;
elementName = elementName + node.tagName + "/r/n";
}
方法一
var childrens = node.childNodes;
for(var i = 0 ; i < childrens.length; i++){
total += countTotalElement(childrens[i]);
}
方法二
for(var m=node.firstChild; m!=null; m=m.nextSibling){
total += countTotalElement(m);
}

return total;
}
-->
</script>
</head>
<body>
<table width="364" border="1" cellpadding="0" cellspacing="0">
<form action="" name="form1" method="post">
  <tr>
    <td width="20%">&nbsp;用戶名</td>
    <td width="80%">&nbsp;<input type="text" name="input1" value=""></td>
  </tr>
  <tr>
    <td>&nbsp;密碼</td>
    <td>&nbsp;<input type="password" name="password1" value=""></td>
  </tr></form>
</table>
<a href="javascript:void(0)"  onClick="alert('標記總數:' + countTotalElement(document) + '/r/n 全部標記如下 /r/n' + elementName );elementName='';">開始統計</a>
</body>
</html>

二 搜索文檔中特定的元素

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>定位HTML文檔特定節點</title>
<script language="javascript">
<!--
function countTotal(){
var elements = document.getElementsByTagName("input");
window.alert("input類型節點總數是:" + elements.length);
}
function anchorElement(){
var element = document.getElementById("submit");
window.alert("控件的value是:" + element.value);
}
-->
</script>
</head>

<body>
<table width="364" border="1" cellpadding="0" cellspacing="0">
<form action="" name="form1" method="post">
  <tr>
    <td width="20%">&nbsp;用戶名</td>
    <td width="80%">&nbsp;<input type="text" name="input1" value=""></td>
  </tr>
  <tr>
    <td>&nbsp;密碼</td>
    <td>&nbsp;<input type="password" name="password1" value=""></td>
  </tr>
  <tr>
    <td>&nbsp;</td>
    <td><input type="submit" name="Submit" value="提交"></td>
  </tr>
</form>
</table>
<a href="javascript:void(0);" onClick="countTotal();">統計input節點總數</a>
<a href="javascript:void(0);" onClick="anchorElement();">定位提交按鈕</a>
</body>
</html>

三 修改文檔內容

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>顛倒節點順序</title>
<script language="javascript">
<!--
function reverseNode(node){
var kids = node.childNodes;
var kidsNum = kids.length;
for( var i=kidsNum - 1; i>=0;i--){
var c=node.removeChild(kids[i]);
node.appendChild(c);
}
}
-->
</script>
</head>

<body>
<p>第一行</p>
<p>第二行</p>
<p>第三行</p>
<input type="button" name="reverseNode" value="顛倒順序" οnclick="reverseNode(document.body)" />
</body>
</html>

實例二
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>顛倒表格行順序</title>
<script language="javascript">
<!--
function reverserTable(){
var node = document.getElementsByTagName("table")[0];
var child = document.getElementsByTagName("tr");
var newChild = new Array();
for(var i=0;i<child.length;i++){
newChild[i]=child[i].firstChild.innerHTML;
}
node.removeChild(node.childNodes[0]);
var header=node.createTHead();
for(var i=0;i<newChild.length;i++){
var headerrow=header.insertRow(i);
var Cell=headerrow.insertCell(0);
Cell.appendChild(document.createTextNode(newChild[newChild.length-i-1]));
}
}
-->
</script>
</head>
<body>
<table width="200" border="0">
  <thead>
  <tr>
    <td>第一行</td>
  </tr>
  <tr>
    <td>第二行</td>
  </tr>
  <tr>
    <td>第三行</td>
  </tr>
  <tr>
    <td>第四行</td>
  </tr>
  </thead>
</table>
<input type="button" name="reverse" value="開始顛倒"  onClick="reverserTable();">
</body>
</html>

實例三
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>顛倒表格行順序</title>
<script language="javascript">
<!--
function replaceContent(){
var table1 = document.getElementsByTagName("table")[0];
var table2 = document.getElementsByTagName("table")[1];
var kid1 = table1.firstChild.firstChild.firstChild;
var kid2 = table2.firstChild.firstChild.firstChild;
var repKid = kid2.firstChild;
try{
kid1.replaceChild(repKid,kid1.firstChild);
}
catch(e){
alert(e);
}
}
-->
</script>
</head>
<body>
<table width="200" border="0">
  <tbody>
  <tr>
    <td>表格一</td>
  </tr>
  </tbody>
</table>
<table width="200" border="0">
  <tbody>
  <tr>
    <td>表格二</td>
  </tr>
  </tbody>
</table>
<input type="button" name="reverse" value="開始顛倒"  onClick="replaceContent();">
</body>
</html>


四 往文檔中添加新內容
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>添加表格內容</title>
<script language="javascript">
<!--
function insertStr(){
var f = document.form1;
var value = f.str.value;
var text = document.createTextNode(value);
var td = document.createElement("td");
var tr = document.createElement("tr");
var tbody = document.createElement("tbody");
td.appendChild(text);
tr.appendChild(td);
tbody.appendChild(tr);
var parNode = document.getElementById("table1");
parNode.insertBefore(tbody,parNode.firstChild);
}
-->
</script>
</head>
<body>
<form name="form1" method="post" action="">
<input type="text" id="str" name="str" value="" />
<input type="button" name="insert" value="留言" id="insert" οnclick="insertStr();" />
</form>
<table width="400" border="0" cellspacing="0" cellpadding="0" id="table1">
<tbody>
  <tr>
    <td  height="25">網友留言列表:</td>
  </tr>
  </tbody>
</table>

</body>
</html>

五 使用DOM對象操作XML文檔
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>加載XML文檔</title>
<script language="javascript">
<!--
function loadXml(handler){
var url = "employees.xml";
if(document.implementation && document.implementation.createDocument){
var xmldoc = document.implementation.createDocument("","",null);
xmldoc.οnlοad=handler(xmldoc,null);
xmldoc.load(url);
}
else if(window.ActiveXObject){
var xmldoc = new ActiveXObject("Microsoft.XMLDOM");
xmldoc.onreadystatechange=function(){
if(xmldoc.readyState==4)handler(xmldoc,url);
}
xmldoc.load(url);
}
}

function makeTalbe(xmldoc,url){
var talbe = document.createElement("table");
table.setAttribute("border",1);
table.setAttribute("width","600");
table.setAttribute("class","tab-content");
document.body.appendChild(table);
var caption = "Employee Data from " + url;
table.createCaption().appendChild(document.createTextNode(caption));
var header = table.createThead();
var headerrow = header.insertRow(0);
headerrow.insertCell(0).appendChild(document.createTextNode("姓名"));
headerrow.insertCell(1).appendChild(document.createTextNode("職業"));
headerrow.insertCell(2).appendChild(document.createTextNode("工資"));
var employees = xmldoc.getElementsByTagName("employees)";
for(var i=0;i<employees.length;i++){
var e = employees[i];
var name = e.getAttribute("name");
var job = e.getElementsByTagName("job")[0].firstChild.data;
var salary = e.getElementsByTagName("salary")[0].firstChild.data;
var row=table.insertRow(i+1);
row.insertCell(0).appendChild(document.createTextNode(name));
row.insertCell(1).appendChild(document.createTextNode(job));
row.insertCell(2).appendChild(document.createTextNode(salary));
}
}
-->
</script>
</head>

<body onLoad="loadXml(makeTalbe);">
</body>
</html>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章