遍歷是指透過或遍歷節點樹
遍歷節點樹
通常,您想要迴圈一個 XML 文件,例如:當您想要提取每個元素的值時。
這被稱為"遍歷節點樹"。
下面的示例迴圈遍歷所有 <book>
的子節點,並顯示它們的名稱和值:
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
var x, i ,xmlDoc;
var txt = "";
var text = "<book>" +
"<title>Everyday Italian</title>" +
"<author>Giada De Laurentiis</author>" +
"<year>2005</year>" +
"</book>";
parser = new DOMParser();
xmlDoc = parser.parseFromString(text,"text/xml");
// documentElement 總是代表根節點
x = xmlDoc.documentElement.childNodes;
for (i = 0; i < x.length ;i++) {
txt += x[i].nodeName + ": " + x[i].childNodes[0].nodeValue + "<br>";
}
document.getElementById("demo").innerHTML = txt;
</script>
</body>
</html>
輸出:
title: Everyday Italian
author: Giada De Laurentiis
year: 2005
示例解釋
- 將 XML 字串載入到
xmlDoc
中 - 獲取根元素的子節點
- 對於每個子節點,輸出節點名稱和文字節點的節點值
瀏覽器中 DOM 解析的差異
瀏覽器之間存在一些差異。其中一個重要的差異是:
- 它們如何處理空格和換行符
DOM - 空格和換行符
XML 經常包含節點之間的換行符或空格字元。當文件由簡單編輯器(如記事本)編輯時,通常會出現這種情況。
以下示例(由記事本編輯)在每行之間包含 CR/LF(換行符),並在每個子節點之前包含兩個空格:
<book>
<title>Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
Internet Explorer 9 及更早版本不會將空格或換行符視為空格文字節點,而其他瀏覽器會。
以下示例將輸出根元素(books.xml)的子節點數。IE9 及更早版本將輸出 4 個子節點,而 IE10 及更高版本以及其他瀏覽器將輸出 9 個子節點:
function myFunction(xml) {
var xmlDoc = xml.responseXML;
x = xmlDoc.documentElement.childNodes;
document.getElementById("demo").innerHTML =
"Number of child nodes: " + x.length;
}
PCDATA - 解析字元資料
XML 解析器通常解析 XML 文件中的所有文字。
當解析 XML 元素時,還會解析 XML 標籤之間的文字:
<message>This text is also parsed</message>
解析器執行此操作是因為 XML 元素可以包含其他元素,如此示例中的 <name>
元素包含兩個其他元素(first 和 last):
<name><first>Bill</first><last>Gates</last></name>
解析器將其拆分為子元素,如下所示:
<name>
<first>Bill</first>
<last>Gates</last>
</name>
解析字元資料(PCDATA)是一個用於指代將由 XML 解析器解析的文字資料的術語。
CDATA - 未解析的字元資料
術語 CDATA 用於指代 XML 解析器不應解析的文字資料。
字元如 "<" 和 "&" 在 XML 元素中是非法的。
"<" 會生成錯誤,因為解析器將其解釋為新元素的開始。
"&" 會生成錯誤,因為解析器將其解釋為字元實體的開始。
一些文字,比如 JavaScript 程式碼,包含許多 "<" 或 "&" 字元。為了避免錯誤,可以將指令碼程式碼定義為 CDATA。
CDATA 部分中的所有內容都會被解析器忽略。
CDATA 部分以 "" 結束:
<script>
<![CDATA[
function matchwo(a,b) {
if (a < b && a < 0) {
return 1;
} else {
return 0;
}
}
]]>
</script>
在上面的示例中,CDATA 部分內的所有內容都會被解析器忽略。
關於 CDATA 部分的注意事項:
- CDATA 部分不能包含字串 "]]>"。不允許巢狀 CDATA 部分。
- 表示 CDATA 部分結束的 "]]>" 不能包含空格或換行符。
XML DOM - 導航節點
可以使用節點之間的關係來導航節點。
導航 DOM 節點
透過節點之間的關係在節點樹中訪問節點
,通常被稱為"導航節點"。
在 XML DOM 中,節點關係被定義為節點的屬性:
parentNode
childNodes
firstChild
lastChild
nextSibling
previousSibling
以下影像說明了 books.xml 中的節點樹的一部分以及節點之間的關係:
DOM - 父節點
所有節點都有一個父節點。以下程式碼導航到 <book>
的父節點:
function myFunction(xml) {
var xmlDoc = xml.responseXML;
var x = xmlDoc.getElementsByTagName("book")[0];
document.getElementById("demo").innerHTML = x.parentNode.nodeName;
}
示例解釋:
- 將 books.xml 載入到
xmlDoc
中 - 獲取第一個
<book>
元素 - 輸出 "x" 的父節點的節點名稱
避免空文字節點
某些瀏覽器可能將空白空格或換行符視為文字節點。
在使用屬性如 firstChild
、lastChild
、nextSibling
、previousSibling
時,這會導致問題。
為了避免導航到空文字節點(元素節點之間的空格和換行符),我們使用一個檢查節點型別的函式:
function get_nextSibling(n) {
var y = n.nextSibling;
while (y.nodeType != 1) {
y = y.nextSibling;
}
return y;
}
上述函式允許您使用 get_nextSibling(node)
而不是屬性 node.nextSibling
。
程式碼解釋:
- 元素節點的型別為 1。如果兄弟節點不是元素節點,則移動到下一個節點,直到找到一個元素節點。
- 獲取下一個是元素節點的兄弟節點。
獲取第一個子元素
以下程式碼顯示了第一個 <book>
的第一個元素節點:
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xhttp.open("GET", "books.xml", true);
xhttp.send();
function myFunction(xml) {
var xmlDoc = xml.responseXML;
var x = get_firstChild(xmlDoc.getElementsByTagName("book")[0]);
document.getElementById("demo").innerHTML = x.nodeName;
}
// 檢查第一個節點是否是元素節點
function get_firstChild(n) {
var y = n.firstChild;
while (y.nodeType != 1) {
y = y.nextSibling;
}
return y;
}
</script>
</body>
</html>
輸出:
title
示例解釋
- 將 books.xml 載入到
xmlDoc
中 - 在第一個
<book>
元素節點上使用get_firstChild
函式,獲取第一個子節點,該子節點是一個元素節點 - 輸出是第一個是元素節點的子節點的節點名稱
更多示例
lastChild()
: 使用lastChild()
方法和自定義函式獲取節點的最後一個子節點。nextSibling()
: 使用nextSibling()
方法和自定義函式獲取節點的下一個兄弟節點。previousSibling()
: 使用previousSibling()
方法和自定義函式獲取節點的前一個兄弟節點。
XML DOM 獲取節點值
nodeValue
屬性用於獲取節點的文字值。
getAttribute()
方法返回屬性的值。
獲取元素的值
在 DOM 中,一切都是節點。元素節點沒有文字值。元素節點的文字值儲存在子節點中,這個節點被稱為文字節點。要檢索元素的文字值,必須檢索元素的文字節點的值。
getElementsByTagName 方法
getElementsByTagName()
方法按照它們在源文件中出現的順序,返回指定標籤名的所有元素的節點列表。假設 books.xml
已載入到 xmlDoc
。
此程式碼檢索第一個 <title>
元素:
var x = xmlDoc.getElementsByTagName("title")[0];
childNodes 屬性
childNodes
屬性返回元素的所有子節點的列表。以下程式碼檢索第一個 <title>
元素的文字節點:
x = xmlDoc.getElementsByTagName("title")[0];
y = x.childNodes[0];
nodeValue 屬性
nodeValue
屬性返回文字節點的文字值。以下程式碼檢索第一個 <title>
元素的文字節點的文字值:
x = xmlDoc.getElementsByTagName("title")[0];
y = x.childNodes[0];
z = y.nodeValue;
結果在 z
中:"Everyday Italian"
完整示例
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xhttp.open("GET", "books.xml", true);
xhttp.send();
function myFunction(xml) {
var xmlDoc = xml.responseXML;
var x = xmlDoc.getElementsByTagName('title')[0];
var y = x.childNodes[0];
document.getElementById("demo").innerHTML = y.nodeValue;
}
</script>
</body>
</html>
迴圈遍歷所有 <title>
元素
獲取屬性值
在 DOM 中,屬性也是節點。與元素節點不同,屬性節點具有文字值。獲取屬性值的方式是獲取其文字值。
獲取屬性值 - getAttribute()
getAttribute()
方法返回屬性的值。以下程式碼檢索第一個 <title>
元素的 "lang" 屬性的文字值:
x = xmlDoc.getElementsByTagName("title")[0];
txt = x.getAttribute("lang");
結果在 txt
中:"en"
迴圈遍歷所有 <book>
元素並獲取它們的 "category"
獲取屬性值 - getAttributeNode()
getAttributeNode()
方法返回屬性節點。以下程式碼檢索第一個 <title>
元素的 "lang" 屬性的文字值:
x = xmlDoc.getElementsByTagName("title")[0];
y = x.getAttributeNode("lang");
txt = y.nodeValue;
XML DOM 更改節點值
nodeValue
屬性用於更改節點的值。
setAttribute()
方法用於更改屬性值。
更改元素的值
在 DOM 中,一切都是節點。元素節點沒有文字值。元素節點的文字值儲存在子節點中,這個節點被稱為文字節點。要更改元素的文字值,必須更改元素的文字節點的值。
更改文字節點的值
nodeValue
屬性可用於更改文字節點的值。此程式碼更改第一個 <title>
元素的文字節點值:
xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue = "new content";
示例解釋:
- 假設
books.xml
已載入到xmlDoc
。 - 獲取
<title>
元素的第一個子節點。 - 將節點值更改為 "new content"。
迴圈遍歷並更改所有 <title>
元素的文字節點
更改屬性的值
在 DOM 中,屬性也是節點。與元素節點不同,屬性節點具有文字值。更改屬性值的方式是更改其文字值。
使用 setAttribute()
更改屬性
setAttribute()
方法更改屬性的值。如果屬性不存在,則會建立一個新屬性。
此程式碼更改 <book>
元素的 category
屬性:
xmlDoc.getElementsByTagName("book")[0].setAttribute("category", "food");
示例解釋:
- 假設
books.xml
已載入到xmlDoc
。 - 獲取第一個
<book>
元素。 - 將 "category" 屬性值更改為 "food"。
迴圈遍歷所有 <title>
元素並新增
使用 nodeValue
更改屬性
nodeValue
屬性是屬性節點的值。更改 value
屬性會更改屬性的值。
xmlDoc.getElementsByTagName("book")[0].getAttributeNode("category").nodeValue = "food";
示例解釋:
- 假設
books.xml
已載入到xmlDoc
。 - 獲取第一個
<book>
元素的 "category" 屬性。 - 將屬性節點的值更改為 "food"。
XML DOM 刪除節點
刪除元素節點
removeChild()
方法刪除指定的節點。當刪除節點時,它的所有子節點也會被刪除。
此程式碼將從載入的 xml 中刪除第一個 <book>
元素:
y = xmlDoc.getElementsByTagName("book")[0];
xmlDoc.documentElement.removeChild(y);
示例解釋:
- 假設
books.xml
已載入到xmlDoc
。 - 將變數
y
設定為要刪除的元素節點。 - 使用
removeChild()
方法從父節點中刪除元素節點。
刪除自己 - 刪除當前節點
removeChild()
方法是刪除指定節點的唯一方法。當您導航到要刪除的節點時,可以使用 parentNode
屬性和 removeChild()
方法來刪除該節點:
x = xmlDoc.getElementsByTagName("book")[0];
x.parentNode.removeChild(x);
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 將變數
y
設定為要刪除的元素節點。 - 使用
parentNode
屬性和removeChild()
方法刪除元素節點。
刪除文字節點
removeChild()
方法也可以用於刪除文字節點:
x = xmlDoc.getElementsByTagName("title")[0];
y = x.childNodes[0];
x.removeChild(y);
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 將變數
x
設定為第一個title
元素節點。 - 將變數
y
設定為要刪除的文字節點。 - 使用
removeChild()
方法從父節點中刪除元素節點。
使用 removeChild()
僅僅為了刪除節點的文字不是很常見。可以使用 nodeValue
屬性代替。請參閱下一段。
清除文字節點
nodeValue
屬性可用於更改文字節點的值:
xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue = "";
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 獲取第一個
title
元素的第一個子節點。 - 使用
nodeValue
屬性清除文字節點的文字。
透過名稱刪除屬性節點
removeAttribute()
方法按名稱刪除屬性節點。
示例: removeAttribute('category')
此程式碼將刪除第一個 <book>
元素中的 "category" 屬性:
x = xmlDoc.getElementsByTagName("book");
x[0].removeAttribute("category");
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 使用
getElementsByTagName()
獲取book
節點。 - 從第一個
book
元素節點中刪除 "category" 屬性。
迴圈遍歷並刪除所有 <book>
元素的 "category"
透過物件刪除屬性節點
removeAttributeNode()
方法使用節點物件作為引數刪除屬性節點。
示例: removeAttributeNode(x)
此程式碼將刪除所有 <book>
元素的所有屬性:
x = xmlDoc.getElementsByTagName("book");
for (i = 0; i < x.length; i++) {
while (x[i].attributes.length > 0) {
attnode = x[i].attributes[0];
old_att = x[i].removeAttributeNode(attnode);
}
}
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 使用
getElementsByTagName()
獲取所有book
節點。 - 對於每個
book
元素,檢查是否有任何屬性。 - 在
book
元素中存在屬性時,刪除屬性
XML DOM 新增節點
新增節點 - appendChild()
appendChild()
方法將子節點新增到現有節點。新節點在任何現有子節點之後被新增(追加)。注意:如果節點的位置很重要,請使用 insertBefore()
。
此程式碼片段建立一個元素(<edition>
),並將其新增在第一個 <book>
元素的最後一個子節點之後:
newEle = xmlDoc.createElement("edition");
xmlDoc.getElementsByTagName("book")[0].appendChild(newEle);
示例解釋:
- 假設
books.xml
已載入到xmlDoc
。 - 建立一個新節點
<edition>
。 - 將節點追加到第一個
<book>
元素。
此程式碼片段與上述相同,但新元素新增了一個值:
newEle = xmlDoc.createElement("edition");
newText = xmlDoc.createTextNode("first");
newEle.appendChild(newText);
xmlDoc.getElementsByTagName("book")[0].appendChild(newEle);
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 建立一個新節點
<edition>
。 - 建立一個新文字節點 "first"。
- 將文字節點追加到
<edition>
節點。 - 將
<edition>
節點追加到<book>
元素。
插入節點 - insertBefore()
insertBefore()
方法在指定的子節點之前插入一個節點。當新增的節點的位置很重要時,此方法很有用:
newNode = xmlDoc.createElement("book");
x = xmlDoc.documentElement;
y = xmlDoc.getElementsByTagName("book")[3];
x.insertBefore(newNode, y);
示例解釋:
- 假設
books.xml
已載入到xmlDoc
。 - 建立一個新元素節點
<book>
。 - 在最後一個
<book>
元素節點之前插入新節點。 - 如果
insertBefore()
的第二個引數為null
,新節點將在最後一個現有子節點之後新增。x.insertBefore(newNode, null)
和x.appendChild(newNode)
都將向x
新增一個新的子節點。
新增新屬性
setAttribute()
方法設定屬性的值:
xmlDoc.getElementsByTagName('book')[0].setAttribute("edition", "first");
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 為第一個
<book>
元素的 "edition" 屬性設定值為 "first"。
注意: 沒有名為 addAttribute()
的方法。如果屬性不存在,setAttribute()
將建立一個新屬性。如果屬性已存在,setAttribute()
方法將覆蓋現有值。
向文字節點新增文字 - insertData()
insertData()
方法將資料插入現有文字節點。insertData()
方法有兩個引數:offset
- 開始插入字元的位置(從零開始),string
- 要插入的字串。
以下程式碼片段將 "Easy" 新增到已載入 XML 的第一個 <title>
元素的文字節點中:
xmlDoc.getElementsByTagName("title")[0].childNodes[0].insertData(0, "Easy ");
XML DOM 克隆節點
克隆節點
cloneNode()
方法建立指定節點的副本。cloneNode()
方法有一個引數(true 或 false)。此引數指示克隆的節點是否應包括原始節點的所有屬性和子節點。
以下程式碼片段複製第一個 <book>
節點並將其附加到文件的根節點:
oldNode = xmlDoc.getElementsByTagName('book')[0];
newNode = oldNode.cloneNode(true);
xmlDoc.documentElement.appendChild(newNode);
結果
Everyday Italian
Harry Potter
XQuery Kick Start
Learning XML
Everyday Italian
示例解釋
- 假設
books.xml
已載入到xmlDoc
。 - 獲取要複製的節點(
oldNode
)。 - 將節點克隆到 "newNode"。
- 將新節點附加到 XML 文件的根節點。
最後
為了方便其他裝置和平臺的小夥伴觀看往期文章:
微信公眾號搜尋:Let us Coding
,關注後即可獲取最新文章推送
看完如果覺得有幫助,歡迎點贊、收藏、關注