当前位置: 首页 > news >正文

跟着 MDN 学 HTML day_52:(深入 XPathExpression 接口)

在前一篇文章中,我们详细学习了 XPathEvaluator 接口,了解了如何创建 XPath 表达式求值器并执行查询。今天我们将继续深入,探讨 XPathExpression 接口。这个接口代表一个已经编译好的 XPath 表达式,可以被重复用于在文档或特定节点上进行求值。

XPathExpression 的核心价值在于预编译。当我们需要在应用中多次执行同一个 XPath 查询时,使用预编译的表达式对象可以避免每次重新解析 XPath 字符串,从而提升性能。同时,表达式中出现的所有命名空间前缀也会被预先解析,进一步提高了效率。

一、XPathExpression 概述

XPathExpression 是一个基线广泛可用的接口,它代表一个经过编译的 XPath 表达式。编译后的表达式可以在文档或特定节点上求值,以从 DOM 树中获取所需的信息。

这个接口特别适用于表达式会被重复使用的场景。由于表达式只编译一次,所有内部的前缀和路径解析工作都会在编译阶段完成,后续每次求值时就可以直接使用,大幅减少了重复计算的开销。

示例:XPathExpression 的创建和基本使用

<div>XPath example</div><div>Number of<div>s:<output></output></div>
constxpath="//div";constevaluator=newXPathEvaluator();// 通过 createExpression 创建编译后的表达式对象constexpression=evaluator.createExpression(xpath);// 在文档上执行编译后的表达式constresult=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);// 显示匹配的 div 数量document.querySelector("output").textContent=result.snapshotLength;

在这个例子中,我们首先创建了一个 XPathEvaluator 实例,然后通过 createExpression() 方法将字符串 “//div” 编译为一个 XPathExpression 对象。之后,调用该对象的 evaluate() 方法即可在文档上执行查询。

二、XPathExpression.evaluate() 方法

定义

evaluate() 是 XPathExpression 接口唯一的实例方法。它用于在给定的节点或文档上执行编译好的 XPath 表达式,并返回一个 XPathResult 对象。

语法

evaluate(contextNode)evaluate(contextNode,type)evaluate(contextNode,type,result)

参数说明:

contextNode:作为求值上下文的节点,表达式将相对于此节点进行求值

type:可选,指定返回结果的类型,必须是 XPathResult 的常量之一

result:可选,允许指定一个可复用的结果对象。如果传入 null 或实现不支持复用,将返回新的结果对象

示例:在不同上下文中使用 evaluate

<divclass="container"><pclass="item">第一项</p><pclass="item">第二项</p></div><divclass="container"><pclass="item">第三项</p></div><div>总计:<outputid="total"></output></div><div>第一个容器:<outputid="first"></output></div>
constevaluator=newXPathEvaluator();// 编译表达式:查找所有 class 为 item 的 p 元素constexpression=evaluator.createExpression("//p[@class='item']");// 在整个文档上求值consttotalResult=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);document.getElementById("total").textContent=totalResult.snapshotLength;// 输出:3// 在第一个容器上求值constfirstContainer=document.querySelector(".container");constfirstResult=expression.evaluate(firstContainer,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);document.getElementById("first").textContent=firstResult.snapshotLength;// 输出:2

这个例子清晰地展示了编译后的表达式可以在不同的上下文节点上执行,每次都能得到相对于该上下文的正确结果。这种灵活性使得 XPathExpression 在处理复杂 DOM 结构时非常实用。

三、evaluate 方法的返回值

evaluate() 方法始终返回一个 XPathResult 对象,该对象的具体结构取决于 type 参数的设置。了解不同的结果类型,能帮助我们更准确地获取所需数据。

常用结果类型回顾

XPathResult.NUMBER_TYPE:返回数值,通过 numberValue 属性获取

XPathResult.STRING_TYPE:返回字符串,通过 stringValue 属性获取

XPathResult.BOOLEAN_TYPE:返回布尔值,通过 booleanValue 属性获取

XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:返回节点快照列表

XPathResult.FIRST_ORDERED_NODE_TYPE:返回第一个匹配节点

XPathResult.ANY_TYPE:由表达式自然决定类型

示例:使用不同类型获取结果

<divclass="product"data-price="50">键盘</div><divclass="product"data-price="120">鼠标</div><divclass="product"data-price="80">耳机</div><div><p>商品数量:<outputid="count"></output></p><p>总价格:<outputid="sum"></output></p><p>第一个商品:<outputid="first-product"></output></p><p>有超过100元的商品:<outputid="has-expensive"></output></p></div>
constevaluator=newXPathEvaluator();// 编译多个表达式用于不同目的constcountExpression=evaluator.createExpression("count(//div[@class='product'])");constsumExpression=evaluator.createExpression("sum(//div[@class='product']/@data-price)");constfirstExpression=evaluator.createExpression("//div[@class='product'][1]");constboolExpression=evaluator.createExpression("boolean(//div[@data-price > 100])");// 获取商品数量constcountResult=countExpression.evaluate(document,XPathResult.NUMBER_TYPE);document.getElementById("count").textContent=countResult.numberValue;// 输出:3// 获取总价格constsumResult=sumExpression.evaluate(document,XPathResult.NUMBER_TYPE);document.getElementById("sum").textContent=sumResult.numberValue;// 输出:250// 获取第一个商品的文本constfirstResult=firstExpression.evaluate(document,XPathResult.STRING_TYPE);document.getElementById("first-product").textContent=firstResult.stringValue;// 输出:键盘// 判断是否存在超过100元的商品constboolResult=boolExpression.evaluate(document,XPathResult.BOOLEAN_TYPE);document.getElementById("has-expensive").textContent=boolResult.booleanValue?"是":"否";// 输出:是

通过预编译不同的表达式并指定合适的结果类型,我们可以高效地获取各种形式的结果。这种预编译加多类型返回的模式,让 XPath 查询在实际应用中非常灵活。

四、异常处理

在调用 evaluate() 方法时,如果传入的参数不符合要求,可能会抛出多种类型的 DOMException 异常。了解这些异常有助于我们编写更健壮的代码。

常见异常类型

INVALID_EXPRESSION_ERR:表达式不合法

TYPE_ERR:结果无法转换为指定的类型

NAMESPACE_ERR:表达式中的命名空间前缀无法解析

WRONG_DOCUMENT_ERR:提供的上下文节点来自不被支持的文档

NOT_SUPPORTED_ERR:上下文节点类型不被允许或请求类型不被允许

示例:捕获 evaluate 方法的异常

constevaluator=newXPathEvaluator();constexpression=evaluator.createExpression("//div");// 正常执行try{constresult=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);console.log("执行成功,找到节点数:",result.snapshotLength);}catch(e){console.error("执行失败:",e.name,e.message);}// 传入非法类型参数try{constresult=expression.evaluate(document,999// 非法的类型值);}catch(e){console.error("类型错误:",e.name);// 输出:类型错误:NotSupportedError 或 TypeError}

在实际开发中,特别是当 XPath 表达式来自用户输入或外部配置时,做好异常捕获非常重要,可以防止程序因为无效的表达式而崩溃。

五、结果对象复用

evaluate() 方法的第三个参数允许我们传入一个已有的 XPathResult 对象。如果浏览器实现支持,这个方法可以复用该对象来存储结果,从而减少垃圾回收的压力,在需要频繁执行查询的场景下能够提升性能。

示例:尝试复用结果对象

<divclass="item">A</div><divclass="item">B</div><divclass="item">C</div><divclass="item">D</div><div>当前数量:<outputid="result-display"></output></div>
constevaluator=newXPathEvaluator();constexpression=evaluator.createExpression("//div[@class='item']");// 创建一个可复用的结果对象letreusableResult=null;// 第一次求值reusableResult=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,reusableResult);console.log("第一次结果对象:",reusableResult);// 第二次求值,尝试复用同一个结果对象reusableResult=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,reusableResult);console.log("第二次结果对象:",reusableResult);// 显示结果document.getElementById("result-display").textContent=reusableResult.snapshotLength;// 输出:4

需要注意的是,并非所有浏览器实现都会真正复用传入的结果对象。如果浏览器不支持复用,方法会创建一个新的结果对象并返回。因此,在编写代码时应始终使用返回值来获取结果,而不是假设传入的对象已经被修改。

六、XPathExpression 与动态数据

在实际应用中,我们经常需要在数据发生变化后重新查询 DOM。预编译的 XPathExpression 对象特别适合这种场景,因为表达式本身不会改变,只需要在新的上下文中重新调用 evaluate() 即可。

示例:动态添加元素后重新求值

<buttonid="add-btn">添加项目</button><buttonid="count-btn">统计项目数</button><ulid="list"><liclass="task">初始任务</li></ul><div>任务总数:<outputid="task-count"></output></div>
constevaluator=newXPathEvaluator();constexpression=evaluator.createExpression("//li[@class='task']");// 更新任务统计functionupdateTaskCount(){constresult=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);document.getElementById("task-count").textContent=result.snapshotLength;}// 添加新任务document.getElementById("add-btn").addEventListener("click",()=>{constnewTask=document.createElement("li");newTask.className="task";newTask.textContent=`任务${Date.now()}`;document.getElementById("list").appendChild(newTask);});// 点击按钮时统计任务数document.getElementById("count-btn").addEventListener("click",updateTaskCount);// 初始统计updateTaskCount();

在这个示例中,我们只在开始时编译了一次 XPath 表达式。之后每次点击统计按钮时,都使用同一个表达式对象重新求值,获取最新的任务列表。这种模式在需要频繁查询 DOM 的应用中非常高效。

七、XPathExpression 的创建与生命周期

XPathExpression 对象通过 XPathEvaluator.createExpression() 方法创建。一旦创建,该对象就可以在整个应用的生命周期中被反复使用,直到不再需要时由垃圾回收机制自动清理。

示例:封装可复用的查询工具

// 创建一个 XPath 查询工具类classXPathQuery{constructor(xpath){this.evaluator=newXPathEvaluator();this.expression=this.evaluator.createExpression(xpath);}// 执行查询并返回节点列表queryNodes(contextNode=document){constresult=this.expression.evaluate(contextNode,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);constnodes=[];for(leti=0;i<result.snapshotLength;i++){nodes.push(result.snapshotItem(i));}returnnodes;}// 执行查询并返回第一个匹配节点queryFirstNode(contextNode=document){constresult=this.expression.evaluate(contextNode,XPathResult.FIRST_ORDERED_NODE_TYPE);returnresult.singleNodeValue;}}// 使用工具类constdivQuery=newXPathQuery("//div");constallDivs=divQuery.queryNodes();console.log("所有 div 数量:",allDivs.length);constfirstDiv=divQuery.queryFirstNode();console.log("第一个 div:",firstDiv);

这种封装方式将编译后的表达式保存在实例中,对外提供简洁的查询接口。在大型应用中,这种方式能够提升代码的可维护性和执行效率。

八、与 XPathEvaluator.evaluate 的对比

在上一篇文章中,我们学习了直接使用 XPathEvaluator.evaluate() 方法执行查询。而 XPathExpression 则提供了一种面向对象的替代方案。两者在功能上是等价的,但在使用方式上有不同的侧重点。

示例:两种方式的对比

<divclass="box">A</div><divclass="box">B</div><div>方式一:<outputid="method1"></output></div><div>方式二:<outputid="method2"></output></div>
constxpath="//div[@class='box']";// 方式一:直接使用 XPathEvaluator.evaluate()constevaluator=newXPathEvaluator();constresult1=evaluator.evaluate(xpath,document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);document.getElementById("method1").textContent=result1.snapshotLength;// 方式二:使用 XPathExpressionconstexpression=evaluator.createExpression(xpath);constresult2=expression.evaluate(document,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);document.getElementById("method2").textContent=result2.snapshotLength;// 两种方式结果相同console.log(result1.snapshotLength===result2.snapshotLength);// true

方式一适合一次性查询,代码更简洁。方式二适合需要多次执行的查询,因为它将表达式的解析和求值分离,可以重复使用编译结果。在性能敏感的场景中,特别是循环或高频操作中,应优先使用 XPathExpression。

九、浏览器兼容性

XPathExpression 接口属于基线广泛可用的特性,在现代主流浏览器中都得到了良好支持,包括 Chrome、Firefox、Safari 和 Edge 的最新版本。

在开发中使用时,如果目标环境包含较旧的浏览器,建议先进行功能检测,或使用 try-catch 包裹相关代码以确保程序的健壮性。对于绝大多数现代 Web 应用来说,可以放心地直接使用这个接口。

十、总结

今天的学习内容聚焦于 XPathExpression 接口,我们从它的定义和创建方式开始,深入学习了 evaluate() 方法的语法、参数、返回值以及异常处理。通过丰富的示例代码,我们看到了预编译表达式在不同上下文中的应用,以及结果对象复用的可能性。

XPathExpression 的核心价值在于将 XPath 查询的编译和求值两个阶段分离,为需要重复查询的场景提供了性能优化。结合 XPathEvaluator 和 XPathResult,这三个接口共同构成了浏览器中完整的 XPath 处理能力。

在实际开发中,当 CSS 选择器无法满足复杂的 DOM 查询需求时,XPathExpression 为我们提供了一个强大而高效的替代方案。掌握这个接口,能够让我们在处理复杂文档结构时更加游刃有余。


想要解锁更多HTML 核心标签实战、前端零基础入门干货、开发避坑全指南吗?
持续关注,后续将更新CSS 布局实战、JavaScript 交互基础、全站导航开发等硬核内容,带你从新手快速进阶,轻松搞定前端开发!

http://www.jsqmd.com/news/824768/

相关文章:

  • 构建AI记忆与技能治理系统:从向量数据库到智能体架构实践
  • ARM JTAG-AP调试架构原理与应用详解
  • Python装包踩坑记:GDAL、OpenCV的whl文件到底去哪找最靠谱?
  • DocSentinel:基于语义关联的代码文档一致性自动化守护方案
  • 模块四-数据转换与操作——26. groupby 基础
  • 量子纠错与错误缓解技术:原理、应用与前沿进展
  • python中的魔法方法
  • 如何用Sabaki快速打开和分析SGF棋谱文件:围棋爱好者的完整指南
  • AI驱动的代码冻结守护者:开源项目xcf如何提升软件发布质量
  • 离婚官司怎么打?2026上海十大离婚纠纷律师排名出炉(5月最新测评) - 外贸老黄
  • 跟着 MDN 学 HTML day_53:(深入理解 XPathResult 接口)
  • 去中心化AI智能体协作网络:SwarmVault架构设计与实践
  • Python人脸识别别再自己造轮子了!用DeepFace三行代码搞定年龄、性别、情绪分析
  • 极客桌面环境配置:从dotfiles到高效工作流
  • 使用HermesAgent对接Taotoken自定义模型供应商
  • Wonder3D:单图3D重建的革命性跨域扩散技术
  • Agent监控管理工具agenttop:实现自动化任务的可观测性与可控性
  • 告别手动画框!用飞桨EISeg 0.5.0,5分钟搞定遥感影像建筑物自动标注
  • Exynos 5420 ISP架构与图像处理技术解析
  • Parabolic:200+网站支持的跨平台视频下载神器
  • ul里能放div吗_列表项嵌套规范说明【说明】
  • CAN总线避坑指南:STM32F103通信异常?先看看TJA1051收发前后的波形对比(CAN_TX vs CAN_RX vs CAN_H)
  • 全球TOP3会展服务商都在用的PlayAI翻译配置模板(含中英日三语字幕同步渲染、唇动延迟补偿参数)
  • Nornir网络自动化监控插件:集成Sentry实现异常告警与上下文追踪
  • 基于CPX与CRICKIT的创客冰淇淋车:电机控制与交互系统实践
  • 机器人多物体抓取:扩散策略与模仿学习的创新应用
  • 别再傻傻分不清了!保姆级图解GPU、CUDA、cuDNN的关系与安装避坑指南
  • 用嘉立创EDA专业版做比赛项目:一个灯光控制器的完整设计复盘与优化思路
  • 无刷电机方波驱动进阶:基于STM32和IR2101S,如何让你的电机转得更稳、停得更准?
  • Godot游戏开发:模块化系统集成与事件驱动架构实战