跟着 MDN 学 HTML day_53:(深入理解 XPathResult 接口)

在前几篇文章中,我们学习了 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 交互基础、全站导航开发等硬核内容,带你从新手快速进阶,轻松搞定前端开发!

相关推荐
摸鱼仙人~1 小时前
html-anything 仓库全面介绍
前端·html
之歆1 小时前
DAY_24JavaScript 面向对象深度全解:Object、构造函数与 this 系统指南(上)
开发语言·前端·javascript·原型模式
梦梦代码精1 小时前
开源智能体平台 BuildingAI 深度解析:Monorepo 架构、MCP 集成及 GPT-Image-2 接入实测
前端·人工智能·后端·gpt·开源·github
Asurplus1 小时前
【VUE】17、使用JSEncrypt对数据加解密
javascript·vue.js·jsencrypt·rsa
fanzhonghong1 小时前
javaWeb开发之前端实战(Tlias案例-部门管理)
前端·后端·web·前后端分离
广州华水科技1 小时前
2026年高口碑GNSS变形监测一体机推荐:提升水库安全解决方案
前端
xiaoxue..1 小时前
讲讲 浏览器的缓存机制
前端·缓存·面试·浏览器
丘比特惩罚陆1 小时前
制作类似aimlab的测试手速反应力的小游戏
开发语言·javascript·visual studio
Data_Journal1 小时前
Node.js网络爬取指南——简单易上手!
大数据·linux·服务器·前端·javascript