DOM 节点遍历:掌握遍历 XML文档结构和内容的技巧
遍历是指通过或遍历节点树
遍历节点树
通常,您想要循环一个 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
,关注后即可获取最新文章推送
看完如果觉得有帮助,欢迎点赞、收藏、关注