<
script?language
=
"
JavaScript
"
>
<!--
var
?doc?
=
?
new
?ActiveXObject(
"
Msxml2.DOMDocument
"
);?
//
ie5.5+,CreateObject("Microsoft.XMLDOM")?
//
加載文檔
//
doc.load("b.xml");
//
創建文件頭
var
?p?
=
?doc.createProcessingInstruction(
"
xml
"
,
"
version='1.0'??encoding='gb2312'
"
);

????
//
添加文件頭
????doc.appendChild(p);

//
用于直接加載時獲得根接點
//
var?root?=?doc.documentElement;
//
兩種方式創建根接點
//
????var?root?=?doc.createElement("students");
????
var
?root?
=
?doc.createNode(
1
,
"
students
"
,
""
);

????
//
創建子接點
????
var
?n?
=
?doc.createNode(
1
,
"
ttyp
"
,
""
);

????????
//
指定子接點文本
????????
//
n.text?=?"?this?is?a?test";
????
????
//
創建孫接點
????
var
?o?
=
?doc.createElement(
"
sex
"
);
????????o.text?
=
?
"
男
"
;????
//
指定其文本
????
//
創建屬性
????
var
?r?
=
?doc.createAttribute(
"
id
"
);
????????r.value
=
"
test
"
;

????????
//
添加屬性
????????n.setAttributeNode(r);

????
//
創建第二個屬性????
????
var
?r1?
=
?doc.createAttribute(
"
class
"
);
????????r1.value
=
"
tt
"
;
????????
????????
//
添加屬性
????????n.setAttributeNode(r1);

????????
//
刪除第二個屬性
????????n.removeAttribute(
"
class
"
);

????????
//
添加孫接點
????????n.appendChild(o);

????????
//
添加文本接點
????????n.appendChild(doc.createTextNode(
"
this?is?a?text?node.
"
));

????????
//
添加注釋
????????n.appendChild(doc.createComment(
"
this?is?a?comment\n
"
));
????
????????
//
添加子接點
????????root.appendChild(n);
????
????
//
復制接點
????
var
?m?
=
?n.cloneNode(
true
);

????????root.appendChild(m);
????????
????????
//
刪除接點
????????root.removeChild(root.childNodes(
0
));

????
//
創建數據段
????
var
?c?
=
?doc.createCDATASection(
"
this?is?a?cdata
"
);
????????c.text?
=
?
"
hi,cdata
"
;
????????
//
添加數據段
????????root.appendChild(c);
????
????
//
添加根接點
????doc.appendChild(root);

????
//
查找接點
????
var
?a?
=
?doc.getElementsByTagName(
"
ttyp
"
);
????
//
var?a?=?doc.selectNodes("http://ttyp");
????
//
顯示改接點的屬性
????
for
(
var
?i
=
?
0
;i
<
a.length;i
++
)

????
{
????????alert(a[i].xml);
????????
for
(
var
?j
=
0
;j
<
a[i].attributes.length;j
++
)

????????
{
????????????alert(a[i].attributes[j].name);
????????}
????}
????
//
修改節點,利用XPATH定位節點
????
var
?b?
=
?doc.selectSingleNode(
"
//ttyp/sex
"
);
????b.text?
=
?
"
女
"
;

????
//
alert(doc.xml);
????
//
XML保存(需要在服務端,客戶端用FSO)
????
//
doc.save();
????
????
//
查看根接點XML
????
if
(n)

????
{
????????alert(n.ownerDocument.xml);
????}
//
-->
</
script
>
??? 在
DOM
眼中,
HTML
跟
XML
一樣是一種樹形結構的文檔,
<html>
是根(
root
)節點,
<head>
、
<title
>
、
<body>
是
<html>
的子(
children
)節點,互相之間是兄弟(
sibling
)節點;
<body>
下面才是子節點
<table>
、
<span>
、
<p>
等等。如下圖:
??? 這個是不是跟
XML
的結構有點相似呢。不同的是,
HTML
文檔的樹形主要包含表示元素、標記的節點和表示文本串的節點。
?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
對象。
使用
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
具體代碼如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>
無標題文檔
</title>
<script language="javascript">
var elementName = ""; //
全局變量,保存
Element
標記名,使用完畢要清空
function countTotalElement(node) { //
參數
node
是一個
Node
對象
?????? var total = 0;
?????? if(node.nodeType == 1) { //
檢查
node
是否為
Element
對象
????????????? total++;???????????????? //
如果是,計數器加
1
????????????? elementName = elementName + node.tagName + "\r\n"; //
保存標記名
?????? }
?????? var childrens = node.childNodes;???????? //
獲取
node
的全部子節點
?????? for(var i=0;i<childrens.length;i++) {
????????????? total += countTotalElement(childrens[i]); //
在每個子節點上進行遞歸操作
?????? }
?????? return total;
}
</script>
</head>
<body>
<a href="javascript:void(0)"
onClick="alert('
標記總數:
' + countTotalElement(document) + '\r\n
全部標記如下:
\r\n' + elementName);elementName='';">
開始統計
</a>
</body>
</html>
運行效果如下:
???????????????
例子
2 – sample3_2.htm
:
接下來使用
firstChile
、
lastChild
、
nextSibling
、
previousSibling
遍歷整個文檔樹。修改一下
countTotalElement
函數,其他跟
sample3_1.htm
一樣:
function countTotalElement(node) { //
參數
node
是一個
Node
對象
?????? var total = 0;
?????? if(node.nodeType == 1) { //
檢查
node
是否為
Element
對象
????????????? total++;???????????????? //
如果是,計數器加
1
????????????? elementName = elementName + node.tagName + "\r\n"; //
保存標記名
?????? }
?????? var childrens = node.childNodes;???????? //
獲取
node
的全部子節點
?????? for(var m=node.firstChild; m!=null;m=m.nextSibling) {
????????????? total += countTotalElement(m); //
在每個子節點上進行遞歸操作
?????? }
?????? return total;
}
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()
方法。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>
無標題文檔
</title>
<script language="javascript">
?????? function reverseNode(node) { //
顛倒節點
node
的順序
????????????? var kids = node.childNodes; //
獲取子節點列表
????????????? var kidsNum = kids.length; //
統計子節點總數
????????????? for(var i=kidsNum-1;i>=0;i--) { //
逆向遍歷子節點列表
???????????????????? var c = node.removeChild(kids[i]); //
刪除指定子節點,保存在
c
中
???????????????????? node.appendChild(c); //
將
c
放在新位置上
????????????? }
?????? }
</script>
</head>
<body>
<p>
第一行
</p>
<p>
第二行
</p>
<p>
第三行
</p>
<p><input type="button" name="reverseGo" value="
顛倒
"
onClick="reverseNode(document.body)"></p>
</body>
</html>
?????????????
例子
4-- sample4_2.htm
:
例子
1
通過直接操作
body
的子節點來修改文檔。在
HTML
文檔中,布局和定位常常通過表格
<table>
來實現。因此,例子
4
將演示操作表格內容,將表格的四個單元行順序顛倒。如果沒有使用
<tbody>
標簽,則
<table>
把全部的
<tr>
當做是屬于一個子節點
<tbody>
,所以我們采用數組緩存的方式,把行數據顛倒一下。這個例子同時也演示了如何使用
DOM
創建表格單元行。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>
無標題文檔
</title>
<script language="javascript">
function reverseTable() {
?????? var node = document.getElementsByTagName("table")[0]; //
第一個表格
?????? var child = node.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); //
在單元行中插入一個單元格
????????????? //
在單元格中創建
TextNode
節點
????????????? cell.appendChild(document.createTextNode(newChild[newChild.length-i-1]));
?????? }
}
</script>
</head>
<body>
<table width="200" border="1" cellpadding="4" cellspacing="0">
??? <tr>
??????? <td height="25">
第一行
</td>
??? </tr>
??? <tr>
??????? <td height="25">
第二行
</td>
??? </tr>
??? <tr>
??????? <td height="25">
第三行
</td>
??? </tr>
??? <tr>
??????? <td height="25">
第四行
</td>
??? </tr>
</table>
<br>
<input type="button" name="reverse" value="
開始顛倒
" onClick="reverseTable()">
</body>
</html>
???? 
例子
5 -- sample4_3.htm
:
正如我們在
Node
節點介紹部分所指出的那樣,
appendChild()
、
replaceChild()
、
removeChild()
、
insertBefore()
方法會立即改變文檔的結構。下面的例子包含兩個表格,我們試著把表格二的內容替換表格一的內容。
<html>
<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; //
定位到
<td>
節點
?????? var kid2 = table2.firstChild.firstChild.firstChild; //
定位到
<td>
節點
?????? var repKid = kid2.firstChild; //
定位到表格二
<td>
內含的
TextNode
節點
?????? try {
????????????? //
用表格二的單元格內容替換表格一的單元格內容,表格二變成沒有單元格內容
????????????? kid1.replaceChild(repKid,kid1.firstChild);
????????????? //
下面注釋如果開放,將出現
object error
,因為表格二已經被改變
????????????? //kid2.replaceChild(kid1.firstChild,kid2.firstChild);
?????? }catch(e){
????????????? alert(e);
?????? }
}
</script>
</head>
<body>
<table width="200" border="1" cellspacing="0" cellpadding="0">
<tbody>
??? <tr>
??????? <td>
表格一
</td>
??? </tr>
</tbody>
</table>
<br>
<table width="200" border="1" cellspacing="0" cellpadding="0">
<tbody>
??? <tr>
??????? <td>
表格二
</td>
??? </tr>
</tbody>
</table>
<br>
<input type="button" name="replaceNode" value="
替換
" onClick="replaceContent()">
</body>
</html>
???? 
注意,當執行
kid1.replaceChild(repKid,kid1.firstChild);
的時候,
table2
的子節點已經被轉移到
table1
了,
table2
已經沒有子節點,不能再調用
table2
的子節點。看看代碼的注釋,試著運行一下,應該就知道文檔是怎么改變的了。
D
、往文檔添加新內容
在學會遍歷、搜索、修改文檔之后,我們現在試著網文檔添加新的內容。其實沒有什么新意,只是利用我們上述提到的
Node
的屬性和方法而已,還是操作
<table>
標記的內容。有新意的是,我們要實現一個留言簿。是的,留言簿,你可以往里面留言,只是不能刷新噢。
例子
6 – sample5_1.htm
:
<html>
<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;
?????? if(value!="") {
????????????? //
從最終的
TextNode
節點開始,慢慢形成
<tbody>
結構
????????????? var text = document.createTextNode(value); //
新建一個
TextNode
節點
????????????? var td = document.createElement("td"); //
新建一個
td
類型的
Element
節點
????????????? var tr = document.createElement("tr"); //
新建一個
tr
類型的
Element
節點
????????????? var tbody = document.createElement("tbody"); //
新建一個
tbody
類型的
Element
節點
????????????? td.appendChild(text); //
將節點
text
加入
td
中
????????????? tr.appendChild(td); //
將節點
td
加入
tr
中
????????????? tbody.appendChild(tr); //
將節點
tr
加入
tbody
中
????????????? var parNode = document.getElementById("table1"); //
定位到
table
上
????????????? parNode.insertBefore(tbody,parNode.firstChild); //
將節點
tbody
插入到節點頂部
????????????? //parNode.appendChild(tbody); //
將節點
tbody
加入節點尾部
?????? }
}
</script>
</head>
<body>
<form name="form1" method="post" action="">
??? <input name="str" type="text" id="str" value="">
??? <input name="insert" type="button" id="insert" value="
留言
" onClick="insertStr()">
</form>
<table width="400" border="1" cellspacing="0" cellpadding="0" id="table1">
<tbody>
??? <tr>
??????? <td height="25">
網友留言列表:
</td>
??? </tr>
</tbody>
</table>
</body>
</html>
我們之前說過,
<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>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>
無標題文檔
</title>
<script language="javascript">
function loadXML(handler) {
?????? var url = "employees.xml";
?????? if(document.implementation&&document.implementation.createDocument) {
????????????? var xmldoc = document.implementation.createDocument("", "", null);
????????????? xmldoc.onload =? handler(xmldoc, url);
????????????? 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 makeTable(xmldoc, url) {
?????? var table = 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("employee");
?????? 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>
<link href="css/style.css" rel="stylesheet" type="text/css">
</head>
?
<body onLoad="loadXML(makeTable)">
</body>
</html>
供讀取調用的
XML
文檔
– employees.xml
:
<?xml version="1.0" encoding="gb2312"?>
<employees>
?????? <employee name="J.Doe">
????????????? <job>Programmer</job>
????????????? <salary>32768</salary>
?????? </employee>
?????? <employee name="A.Baker">
????????????? <job>Sales</job>
????????????? <salary>70000</salary>
?????? </employee>
?????? <employee name="Big Cheese">
????????????? <job>CEO</job>
????????????? <salary>100000</salary>
?????? </employee>
</employees>
處理
XML
文檔
脫離
XML
文檔的
AJAX
是不完整的。在本部分未完成之前,有讀者說
AJAX
改名叫
AJAH
(
H
應該代表
HTML
吧)比較合適。應該承認,
XML
文檔在數據的結構化表示以及接口對接上有先天的優勢,但也不是所有的數據都應該用
XML
表示。有些時候單純的文本表示可能會更合適。下面先舉個
AJAX
處理返回
XML
文檔的例子再討論什么時候使用
XML
。
7.5.1
、處理返回的
XML
例子
8 -- sample7_1.htm
:
在這個例子中,我們采用之前確定的
AJAX
開發框架,稍微修改一下
body
內容和
processRequest
的相應方式,將先前的
employees.xml
的內容讀取出來并顯示。
body
的內容如下:
<input type="button" name="read"
?value="
讀取
XML" onClick="send_request('employees.xml')">
processRequest()
方法修改如下:
?????? //
處理返回信息的函數
??? function processRequest() {
??????? if (http_request.readyState == 4) { //
判斷對象狀態
?????????
??if (http_request.status == 200) { //
信息已經成功返回,開始處理信息
??????????????????????????? var returnObj = http_request.responseXML;
??????????????????????????? var xmlobj = http_request.responseXML;
??????????????????????????? var employees = xmlobj.getElementsByTagName("employee");
??????????????????????????? var feedbackStr = "";
??????????????????????????? for(var i=0;i<employees.length;i++) { //
循環讀取
employees.xml
的內容
?????????????????????????????????? var employee = employees[i];
?????????????????????????????????? feedbackStr += "
員工:
" + employee.getAttribute("name");
?????????????????????????????????? feedbackStr +=
"
職位:
" + employee.getElementsByTagName("job")[0].firstChild.data;
?????????????????????????????????? feedbackStr +=
?"
工資:
" + employee.getElementsByTagName("salary")[0].firstChild.data;
?????????????????????????????????? feedbackStr +=? "\r\n";
??????????????????????????? }
??????????????????????????? alert(feedbackStr);
??????????? } else { //
頁面不正常
??????????????? alert("
您所請求的頁面有異常。
");
??????????? }
??????? }
}
運行一下,看來效果還不錯:

7.5.2
、選擇合適的
XML
生成方式
現在的
web
應用程序往往采用了
MVC
三層剝離的設計方式。
XML
作為一種數據保存、呈現、交互的文檔,其數據往往是動態生成的,通常由
JavaBean
轉換過來。由
JavaBean
轉換成
XML
文檔的方式有好幾種,選擇合適的轉換方式往往能達到事半功倍的效果。下面介紹兩種常用的方式,以便需要的時候根據情況取舍。
A
、類自行序列化成
XML
類自行序列化成
XML
即每個類都實現自己的
toXML()
方法,選擇合適的
API
、適當的
XML
結構、盡量便捷的生成邏輯快速生成相應的
XML
文檔。顯然,這種方式必須要求每個類編寫專門的
XML
生成代碼,每個類只能調用自己的
toXML()
方法。應用諸如
JDOM
等一些現成的
API
,可以減少不少開發投入。例子
9
是一個利用
JDOM
的
API
形成的
toXML()
方法。
例子
9 -- toXml()
的
JDOM
實現
-- Employ
類的
toXml()
方法:
public Element toXml() {?
?????? Element employee = new Element(“employee”);
?????? Employee.setAttribute(“name”,name);
?????? Element jobE = new Element(“job”).addContent(job);
?????? employee.setContent(jobE);
?????? Element salaryE = new Element(“salary”).addContent(salary);
?????? employee.setContent(salaryE);
?????? return employee;
}
JDOM
提供了現成的
API
,使得序列化成
XML
的工作更加簡單,我們只需要把
toXML()
外面包裝一個
Document
,然后使用
XMLOutputter
把文檔寫入
servlet
就可以了。
toXml()
允許遞歸調用其子類的
toXML()
方法,以便生成包含子圖的
XML
文檔。
??????
使用類自行序列化成
XML
的方式,要每個類都實現自己的
toXML()
方法,而且存在數據模型與視圖耦合的問題,即要么為每個可能的視圖編寫獨立的
toXML()
方法,要么心甘情愿接收冗余的數據,一旦數據結構或者文檔發生改變,
toXML()
就要做必要的修改。
B
、頁面模板生成
XML
方式
一般的,可以采用通用的頁面模板技術來生成
XML
文檔,這個
XML
文檔可以符合任何需要的數據模型,供
AJAX
靈活的調用。另外,模板可以采用任何標記語言編寫,提高工作效率。下面是一個采用
Struts
標簽庫編寫的
XML
文檔,輸出之前提到的
employees.xml
:
Sample8_2.jsp
:
<%@ page contentType="application/xml; charset=gb2312" import="Employee"%>
<%@ page import="java.util.Collection,java.util.ArrayList"%>
<?xml version="1.0"?>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%
Employee em1 = new Employee();
em1.setName("J.Doe");
em1.setJob("Programmer");
em1.setSalary("32768");
Employee em2 = new Employee();
em2.setName("A.Baker");
em2.setJob("Sales");
em2.setSalary("70000");
Employee em3 = new Employee();
em3.setName("Big Cheese");
em3.setJob("CEO");
em3.setSalary("100000");
Collection employees = new ArrayList();
employees.add(em1);
employees.add(em2);
employees.add(em3);
pageContext.setAttribute("employees",employees);
%>
<employees>
<logic:iterate name="employees" id="employee">
?????? <employee name="<bean:write name='employee' property='name'/>">
????????????? <job><bean:write name="employee" property="job"/></job>
????????????? <salary><bean:write name="employee" property="salary"/></salary>
?????? </employee>
</logic:iterate>
</employees>
采用頁面模板生成
XML
方式,需要為每個需要的的數據模型建立一個對立的
JSP
文件,用來生成符合規范的
XML
文檔,而不能僅僅在類的
toXML()
方法中組織對象圖來實現。不過,倒是可以更加方便的確保標記匹配、元素和屬性的順序正確以及
XML
實體正確轉義。
參考資料中
Philip McCarthy
的文章還描述了一種
Javascript
對象標注的生成方式,本文在此不贅述。有興趣的讀者可以自行查看了解。
7.5.3
、如何在使用
XML
還是普通文本間權衡
使用
XML
文檔確實有其方便之處。不過
XML
文檔的某些問題倒是要考慮一下,比如說延遲,即服務器不能立即解析
XML
文檔成為
DOM
模型。這個問題在一定程度上會影響
AJAX
要求的快速反應能力。另外,某些情況下我們并不需要使用
XML
來表示數據,比如說數據足夠簡單成只有一個字符串而已。就好像我們之前提到的數據校驗和級聯菜單的例子一樣。所以,個人認為在下面這些情況下可以考慮使用
XML
來作為數據表示的介質:
l????????
數據比較復雜,需要用
XML
的結構化方式來表示
l????????
不用考慮帶寬和處理效率支出
l????????
與系統其他
API
或者其他系統交互,作為一種數據中轉中介
l????????
需要特定各式的輸出視圖而文本無法表示的
總之,要認真評估兩種表示方式的表示成本和效率,選擇合適的合理的表示方式。
在關于
AJAX
的系列文章的下一篇,我們將綜合使用
DOM
和
XML
,來實現一個可以持久化的簡單留言簿。另外,還將試著模擬
MSN Space
的部分功能,來體會
AJAX
的魅力
posted on 2006-09-20 17:28
保爾任 閱讀(164)
評論(0) 編輯 收藏