
本文介绍如何在 xpath 中优雅地实现“优先匹配某条件,若无结果则回退到另一条件”的逻辑,避免 javascript 多次调用 `document.evaluate`,仅用单条 xpath 表达式即可完成首匹配。
在 XPath 中,要实现“先查找 //*[@selected]/@value,若不存在则取 //text() 的第一个匹配项”,关键在于理解 节点集合并与文档顺序。标准 XPath 1.0(浏览器原生支持)虽不支持条件分支语法(如 if-else),但可通过 联合路径 + 位置谓词 [1] 巧妙达成目标。
✅ 正确写法(XPath 1.0 兼容,推荐):
(//*[@selected]/@value | //text())[1]
该表达式含义是:
- //*[@selected]/@value | //text() 构造一个并集节点集(union),包含所有带 selected 属性的元素的 value 属性值,以及所有文本节点;
- (…)[1] 取该并集中按文档顺序排在最前面的节点(即首次出现的匹配项);
- 由于 XPath 节点集默认按文档顺序排序,且 //*[@selected]/@value 中的属性节点在 DOM 中通常早于其父元素内的 //text()(尤其当 selected 出现在靠前的元素上时),因此天然满足“优先匹配 @value”的语义。
⚠️ 注意事项:
- 属性节点(@value)和文本节点(text())属于不同类型,但在 XPath 1.0 中可安全并集;[1] 总是返回文档中最早出现的那个节点,无论类型;
- 若 //*[@selected]/@value 完全不存在,则 [1] 自动取 //text() 中第一个文本节点;
- 不要写作 //*[@selected]/@value | //text()[1] —— 这会先取所有 //text() 的第一个节点(语义错误),再与属性并集,失去“整体首匹配”逻辑。
? 在 JavaScript 中简洁调用示例:
function xpathFirst(path) {
const result = document.evaluate(
path,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
return result.singleNodeValue?.nodeValue ?? result.singleNodeValue?.value;
}
// 一行搞定优先匹配
const value = xpathFirst('(//*[@selected]/@value | //text())[1]');? 进阶提示(XPath 3.1+):
若使用 SaxonJS(Node.js 或现代浏览器中),可启用更清晰的序列语法:
(//*[@selected]/@value, //text())[1]
逗号 , 表示有序序列(非并集),语义更明确:先尝试左侧,再尝试右侧,取首个非空项。但此语法不被原生浏览器 XPath 引擎支持,仅限 SaxonJS 等第三方引擎。
总结:(A | B)[1] 是 XPath 1.0 下实现“首选 A,降级 B”的标准、高效、跨平台方案,无需 JavaScript 逻辑判断,语义简洁,性能最优。










