在前几篇文章中,我们学习了 XPathEvaluator 和 XPathExpression 接口,掌握了如何编译和执行 XPath 表达式。今天我们将聚焦于 XPath 查询的最终产物:XPathResult 接口。
XPathResult 接口代表了在给定节点上下文中对 XPath 表达式求值后生成的结果。由于 XPath 表达式可以产生多种类型的结果,例如数值、字符串、布尔值或节点集合,这个接口提供了相应的属性和方法来处理不同类型的结果。理解 XPathResult 的完整能力,对于高效使用 XPath 至关重要。
一、XPathResult 概述
XPathResult 是一个基线广泛可用的接口,它封装了 XPath 表达式求值后的所有信息。每当调用 evaluate() 方法时,都会返回一个 XPathResult 对象。
这个接口的核心设计理念是:通过 resultType 属性判断结果的具体类型,然后根据类型使用对应的属性来获取值。例如,如果结果是数值类型,就读取 numberValue 属性;如果是布尔类型,就读取 booleanValue 属性。这种设计确保了类型安全的数据访问。
示例:创建 XPathResult 对象
javascript
const xpath = "//div";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
console.log(result instanceof XPathResult); // true
console.log(result.resultType); // 7,对应 ORDERED_NODE_SNAPSHOT_TYPE
console.log(result.snapshotLength); // 文档中 div 的数量
每个 XPath 查询都会返回这样一个结果对象,接下来我们将逐一学习它的各种属性和方法。
二、XPathResult.resultType 属性
定义
resultType 是一个只读属性,返回一个整数值,表示结果的类型。这个整数值对应 XPathResult 接口中定义的类型常量。通过检查 resultType,我们可以在运行时判断结果的具体形式,并采取相应的处理逻辑。
类型常量对照表
| 常量名称 | 值 | 描述 |
|---|---|---|
| ANY_TYPE | 0 | 任意类型,由表达式自然决定 |
| NUMBER_TYPE | 1 | 单个数值结果 |
| STRING_TYPE | 2 | 单个字符串结果 |
| BOOLEAN_TYPE | 3 | 单个布尔值结果 |
| UNORDERED_NODE_ITERATOR_TYPE | 4 | 无序节点迭代器 |
| ORDERED_NODE_ITERATOR_TYPE | 5 | 有序节点迭代器 |
| ORDERED_NODE_SNAPSHOT_TYPE | 6 | 无序节点快照 |
| ORDERED_NODE_SNAPSHOT_TYPE | 7 | 有序节点快照 |
| ANY_UNORDERED_NODE_TYPE | 8 | 任意单个匹配节点 |
| FIRST_ORDERED_NODE_TYPE | 9 | 第一个匹配节点 |
| 示例:判断结果是否为节点集类型 |
html
<div>XPath example</div>
<div>Is XPath result a node set: <output></output></div>
javascript
const xpath = "//div";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ANY_TYPE,
null
);
// 判断结果是否为节点集类型(类型 4 到 9)
const isNodeSet =
result.resultType >= XPathResult.UNORDERED_NODE_ITERATOR_TYPE &&
result.resultType <= XPathResult.FIRST_ORDERED_NODE_TYPE;
document.querySelector("output").textContent = isNodeSet;
// 输出:true
通过 resultType 属性,我们可以在处理结果之前先确认其类型,从而避免访问不匹配的属性时抛出异常。
三、XPathResult.numberValue 属性
定义
numberValue 是一个只读属性,当 resultType 为 NUMBER_TYPE 时,返回结果的数值。这个属性通常与 XPath 的聚合函数(如 count()、sum()、floor() 等)一起使用。
异常说明
如果 resultType 不是 NUMBER_TYPE,访问 numberValue 会抛出 TYPE_ERR 类型的 DOMException。
示例:使用 count 函数统计节点数量
html
<div>XPath example</div>
<div>Number of <div>s: <output></output></div>
javascript
const xpath = "count(//div)";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.NUMBER_TYPE,
null
);
// 获取数值结果
const divCount = result.numberValue;
document.querySelector("output").textContent = divCount;
// 输出:2
在这个例子中,我们使用 XPath 的 count() 函数统计文档中所有
元素的数量。由于指定了 NUMBER_TYPE,结果会以数值形式返回,可以直接通过 numberValue 属性读取。
示例:使用 sum 函数计算总和
html
<div data-value="10">项目A</div>
<div data-value="25">项目B</div>
<div data-value="15">项目C</div>
<div>总和:<output></output></div>
javascript
const xpath = "sum(//div/@data-value)";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.NUMBER_TYPE,
null
);
document.querySelector("output").textContent = result.numberValue;
// 输出:50
XPath 的数值计算能力使得我们在处理数据属性时无需手动遍历节点,一行表达式就能完成统计工作。
四、XPathResult.stringValue 属性
定义
stringValue 是一个只读属性,当 resultType 为 STRING_TYPE 时,返回结果的字符串值。如果 resultType 不是 STRING_TYPE,访问该属性会抛出 TYPE_ERR 异常。
示例:获取节点的文本内容
html
<div class="message">Hello World</div>
<div>文本内容:<output></output></div>
javascript
const xpath = "string(//div[@class='message'])";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.STRING_TYPE,
null
);
document.querySelector("output").textContent = result.stringValue;
// 输出:Hello World
stringValue 属性非常适合用于提取特定节点的文本内容,或者获取属性值的字符串表示。
五、XPathResult.booleanValue 属性
定义
booleanValue 是一个只读属性,当 resultType 为 BOOLEAN_TYPE 时,返回结果的布尔值。这个属性通常与 XPath 的逻辑函数(如 not()、true()、false())或条件表达式一起使用。
示例:判断文本是否匹配
html
<div>XPath example</div>
<p>Text is 'XPath example': <output></output></p>
javascript
const xpath = "//div/text() = 'XPath example'";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.BOOLEAN_TYPE,
null
);
document.querySelector("output").textContent = result.booleanValue;
// 输出:true
在这个例子中,XPath 表达式判断
元素的文本内容是否等于 'XPath example'。由于结果类型指定为 BOOLEAN_TYPE,可以直接通过 booleanValue 获取布尔结果。
示例:判断是否存在特定元素
html
<div class="active">活跃项</div>
<div>存在活跃项:<output></output></div>
javascript
const xpath = "boolean(//div[@class='active'])";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.BOOLEAN_TYPE,
null
);
document.querySelector("output").textContent = result.booleanValue ? "是" : "否";
// 输出:是
布尔类型的查询在条件判断场景中非常实用,可以快速确认某个节点或状态是否存在。
六、XPathResult.singleNodeValue 属性
定义
singleNodeValue 是一个只读属性,返回结果中的单个节点。如果结果类型为 ANY_UNORDERED_NODE_TYPE 或 FIRST_ORDERED_NODE_TYPE,该属性将返回匹配的节点对象。如果没有匹配的节点,返回 null。
示例:获取第一个匹配的节点
html
<div class="item">第一项</div>
<div class="item">第二项</div>
<div class="item">第三项</div>
<div>第一项内容:<output></output></div>
javascript
const xpath = "//div[@class='item']";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const firstNode = result.singleNodeValue;
if (firstNode) {
document.querySelector("output").textContent = firstNode.textContent;
}
// 输出:第一项
singleNodeValue 非常适合只需要获取单个节点的场景,比如获取某个特定配置元素或检查某个节点是否存在。
七、XPathResult.snapshotLength 属性
定义
snapshotLength 是一个只读属性,返回结果快照中的节点数量。当结果类型为 UNORDERED_NODE_SNAPSHOT_TYPE 或 ORDERED_NODE_SNAPSHOT_TYPE 时,这个属性表示匹配节点的总数。
示例:获取节点快照的数量
html
<div class="box">A</div>
<div class="box">B</div>
<div class="box">C</div>
<div>节点总数:<output></output></div>
javascript
const xpath = "//div[@class='box']";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
document.querySelector("output").textContent = result.snapshotLength;
// 输出:3
快照类型的结果一次性捕获了所有匹配节点,之后即使 DOM 发生变化,快照中的数据也不会改变,这为数据的一致性提供了保障。
八、XPathResult.snapshotItem() 方法
定义
snapshotItem(index) 方法返回快照集合中指定索引位置的节点。索引从 0 开始,如果索引超出范围则返回 null。
与迭代器不同,快照不会因为 DOM 的修改而失效,但它可能不再反映当前文档的最新状态。这意味着快照适用于需要稳定数据快照的场景。
示例:遍历快照中的所有节点
html
<ul>
<li class="task">任务1</li>
<li class="task">任务2</li>
<li class="task">任务3</li>
</ul>
<ul id="output-list"></ul>
javascript
const xpath = "//li[@class='task']";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
const outputList = document.getElementById("output-list");
// 遍历快照中的所有节点
for (let i = 0; i < result.snapshotLength; i++) {
const node = result.snapshotItem(i);
const newItem = document.createElement("li");
newItem.textContent = `快照项 ${i}: ${node.textContent}`;
outputList.appendChild(newItem);
}
通过组合使用 snapshotLength 和 snapshotItem(),我们可以方便地遍历所有匹配节点,而不用担心遍历过程中 DOM 发生变化导致的问题。
九、XPathResult.iterateNext() 方法
定义
iterateNext() 方法用于迭代节点集结果中的下一个节点。如果结果类型是 UNORDERED_NODE_ITERATOR_TYPE 或 ORDERED_NODE_ITERATOR_TYPE,每次调用该方法都会返回集合中的下一个节点,直到返回 null 表示迭代结束。
示例:使用迭代器遍历节点
html
<div class="number">一</div>
<div class="number">二</div>
<div class="number">三</div>
<ul id="iter-result"></ul>
javascript
const xpath = "//div[@class='number']";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null
);
const outputList = document.getElementById("iter-result");
let node;
// 使用迭代器依次获取节点
while ((node = result.iterateNext())) {
const newItem = document.createElement("li");
newItem.textContent = `迭代项: ${node.textContent}`;
outputList.appendChild(newItem);
}
迭代器类型的优势在于按需获取节点,不需要一次性创建所有节点的快照,在处理大量节点时可能更节省内存。
十、XPathResult.invalidIteratorState 属性
定义
invalidIteratorState 是一个只读属性,用于指示迭代器是否已失效。当 resultType 为 UNORDERED_NODE_ITERATOR_TYPE 或 ORDERED_NODE_ITERATOR_TYPE 时,如果在结果返回后文档被修改,该属性会变为 true,表示迭代器不再可靠。
示例:检测迭代器状态
html
<div>XPath example</div>
<p>Iterator state: <output></output></p>
javascript
const xpath = "//div";
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ANY_TYPE,
null
);
// 在迭代之前删除一个 div,使迭代器失效
document.querySelector("div").remove();
// 检查迭代器状态
document.querySelector("output").textContent =
result.invalidIteratorState ? "invalid" : "valid";
// 输出:invalid
这个属性的存在提醒我们,迭代器类型的 XPath 结果与 DOM 的当前状态紧密关联。如果预计在获取结果后可能会修改 DOM,使用快照类型(SNAPSHOT)会是更安全的选择。
十一、快照与迭代器的选择
在实际开发中,选择合适的 XPath 结果类型非常重要。快照类型和迭代器类型有各自的适用场景。
示例:对比快照和迭代器的行为
html
<div class="test">A</div>
<div class="test">B</div>
<div class="test">C</div>
<button id="modify-btn">删除第一个节点</button>
<div>
<p>快照结果数量:<output id="snapshot-output"></output></p>
<p>迭代器状态:<output id="iterator-output"></output></p>
</div>
javascript
// 快照类型
const snapshotResult = document.evaluate(
"//div[@class='test']",
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
// 迭代器类型
const iteratorResult = document.evaluate(
"//div[@class='test']",
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null
);
document.getElementById("modify-btn").addEventListener("click", () => {
const firstDiv = document.querySelector(".test");
if (firstDiv) firstDiv.remove();
// 快照数量不变
document.getElementById("snapshot-output").textContent =
snapshotResult.snapshotLength;
// 迭代器状态变为失效
document.getElementById("iterator-output").textContent =
iteratorResult.invalidIteratorState ? "已失效" : "有效";
});
快照适合需要稳定数据视图的场景,迭代器适合只需一次性顺序读取且内存敏感的场合。根据具体需求选择合适的类型,可以让代码更加健壮。
十二、总结
今天的学习内容全面覆盖了 XPathResult 接口的方方面面。我们从 resultType 属性入手,了解了十种结果类型的含义和用途。随后逐一学习了 numberValue、stringValue、booleanValue、singleNodeValue、snapshotLength 等只读属性,以及 snapshotItem() 和 iterateNext() 两个实例方法。最后还探讨了 invalidIteratorState 属性以及快照与迭代器的选择策略。
XPathResult 是 XPath 查询流程的终点,也是我们获取查询结果的关键接口。掌握它的各种属性和方法,能够让我们在处理 XPath 查询结果时更加得心应手,写出更高效、更健壮的代码。
结合前面学习的 XPathEvaluator 和 XPathExpression,现在我们已经完整掌握了浏览器环境中 XPath 查询的整个流程:从创建求值器、编译表达式,到执行查询、处理结果。这套完整的知识体系将为后续的 DOM 高级操作打下坚实的基础。
想要解锁更多HTML 核心标签实战、前端零基础入门干货、开发避坑全指南吗?
持续关注,后续将更新CSS 布局实战、JavaScript 交互基础、全站导航开发等硬核内容,带你从新手快速进阶,轻松搞定前端开发!