登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

JavaScript TreeWalker.currentNode 怎样从指定节点继续遍历

来源:17golang原创

时间:2026-10-27 14:28:11 209浏览 收藏

TreeWalker.currentNode 就是遍历器当前停留的 Node。如果已经通过点击、搜索或业务索引拿到了一个目标节点,可以直接把它赋给 treeWalker.currentNode,然后继续调用 nextNode()、previousNode() 等方法;遍历方向和可见节点仍然受创建 TreeWalker 时的 root、whatToShow 与 filter 约束。

官方文档:https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker/currentNode

恢复遍历的关键不是重新创建一个更大的查询,而是先确认目标节点位于 TreeWalker 的根节点子树内、节点类型没有被过滤掉,再设置 currentNode。设置成功后,后续遍历会从这个游标继续。
要点速览
  • currentNode 读写的是遍历器当前指向的节点,不是固定的根节点。
  • 设置游标不会改变 root、whatToShow 或 filter,只改变下一次遍历的起点。
  • 目标节点不在根节点子树内,或类型被过滤时,不能把它当成当前遍历范围中的有效节点。

先区分 root、currentNode 与过滤规则

创建 TreeWalker 时,root 决定遍历不能越过的树边界;whatToShow 决定哪些节点类型进入候选集合;filter 再对候选节点返回接受、跳过或拒绝。currentNode 则是运行过程中保存的游标,表示 TreeWalker 当前指向的 Node。

成员它决定什么恢复遍历时的检查点
root允许遍历的 DOM 子树上界指定节点必须位于 root 的子树内,不能只看 CSS 选择器是否找到了它
whatToShow节点类型的候选范围元素、文本等类型是否被纳入要与业务目标一致
filter候选节点是否接受或跳过过滤规则不会因为 currentNode 改变而失效
currentNode当前遍历游标设置后,下一次方向方法从该节点附近继续计算
TreeWalker 的 root、过滤规则和 currentNode 游标关系说明图
图1:TreeWalker 的根节点、节点过滤条件与 currentNode 游标关系说明图;这是原创静态结构图,不是浏览器截图。

因此,“从指定节点继续”与“把指定节点改成新的 root”是两件事。前者只移动游标,后者需要重新创建 TreeWalker;如果只是恢复列表中的当前位置,优先使用 currentNode。

创建一个带过滤器的 TreeWalker

下面的例子只遍历 article 内的元素节点,并让过滤器跳过隐藏的 .draft 元素。代码的目标是保留一个稳定的遍历器,后续可以把它的游标恢复到具体的标题节点。

const article = document.querySelector("article");

const treeWalker = document.createTreeWalker(
  article,
  NodeFilter.SHOW_ELEMENT,
  {
    acceptNode(node) {
      // 隐藏草稿不参与目录或可见内容遍历。
      if (node.matches(".draft")) {
        return NodeFilter.FILTER_REJECT;
      }
      return NodeFilter.FILTER_ACCEPT;
    },
  },
);

// TreeWalker 创建后通常从 root 作为 currentNode 开始。
console.log(treeWalker.currentNode === article);

currentNode 的值是一个 Node,这里可以读取它,也可以把另一个节点赋回去。过滤器仍然负责决定后续 nextNode() 或 previousNode() 能到达哪些节点,不能把过滤器当成一次性的初始化选项。

把 currentNode 移到指定节点再继续

假设页面已经定位到了一个标题节点 targetHeading,例如它来自用户点击的目录项或一次普通的 querySelector。把它赋给 currentNode 后,调用 nextNode() 会从这个位置继续寻找下一个符合条件的节点。

function continueFromNode(treeWalker, targetNode) {
  if (!(targetNode instanceof Node)) {
    // 先拒绝 null 或普通对象,避免把错误延后到遍历方法。
    throw new TypeError("targetNode 必须是 DOM Node");
  }

  // 只检查树关系,是否可见仍由 whatToShow 和 filter 决定。
  if (!treeWalker.root.contains(targetNode)) {
    throw new RangeError("目标节点不在 TreeWalker 的 root 子树内");
  }

  // 移动游标;这不会修改 root 或过滤器。
  treeWalker.currentNode = targetNode;

  // 从指定节点之后寻找下一个可接受的元素。
  return treeWalker.nextNode();
}

const targetHeading = article.querySelector("h2[data-section='advanced']");
const nextHeading = continueFromNode(treeWalker, targetHeading);
console.log(nextHeading?.textContent ?? "没有下一个可遍历节点");

这里的返回值可能是另一个 Node,也可能是 null。null 表示沿当前方向已经没有符合条件的节点,不等于设置 currentNode 失败;此时应根据业务决定停止、回退或提示用户。

从指定节点设置 currentNode 后使用 nextNode 和 previousNode 继续遍历的静态关系图
图2:从指定节点恢复 currentNode,再调用 nextNode 或 previousNode 的静态关系说明图;它表达 API 关系,不是运行截图。

处理节点不在遍历范围内的情况

设置 currentNode 前,至少要分开判断三类问题。第一,节点是否位于 root 子树内;第二,节点类型是否被 whatToShow 纳入候选;第三,过滤器是否会接受它。它们分别对应树关系、节点类型和业务过滤,不应混成一个“选择器有没有结果”的判断。

  • 不在 root 子树内:不要把页面另一个区域的节点直接塞给当前遍历器,应该换用覆盖它的 root 或创建新的 TreeWalker。
  • 类型不匹配:例如遍历器只显示元素节点时,文本节点虽然存在于 DOM 中,也不适合作为当前遍历目标来表达元素导航。
  • 被 filter 排除:指定节点可能在树中,但后续方向遍历会跳过它或跳过它的子树,结果仍以过滤器规则为准。
function isElementTarget(treeWalker, node) {
  const elementMask = NodeFilter.SHOW_ELEMENT;
  const isElement = node?.nodeType === Node.ELEMENT_NODE;

  // 这个检查对应 whatToShow 的目标类型,不替代 filter 判断。
  if ((treeWalker.whatToShow & elementMask) === 0 || !isElement) {
    return false;
  }

  // contains 只说明树关系;根节点本身也允许作为当前节点。
  return node === treeWalker.root || treeWalker.root.contains(node);
}

如果业务必须从一个文本节点开始,则创建 TreeWalker 时应选择合适的 whatToShow,并同步调整过滤器和后续代码;不要先用只显示元素的遍历器,再期待它像文本游标一样工作。

整理一个可复用的恢复遍历函数

在目录导航、文章高亮或键盘上下移动等场景中,可以把“检查目标、移动游标、取下一个结果”封装起来。函数不隐藏 null,这样调用方能区分“没有下一个节点”和“恢复参数无效”。

function resumeTreeWalker(treeWalker, targetNode, direction = "next") {
  if (!(treeWalker instanceof TreeWalker)) {
    throw new TypeError("treeWalker 必须是 TreeWalker");
  }
  if (!isElementTarget(treeWalker, targetNode)) {
    throw new RangeError("目标节点不符合当前 TreeWalker 的范围");
  }

  // currentNode 是可写游标,移动它不会重置 root 和过滤条件。
  treeWalker.currentNode = targetNode;

  if (direction === "previous") {
    // previousNode 返回指定节点之前的下一个可接受节点。
    return treeWalker.previousNode();
  }
  if (direction === "next") {
    // nextNode 返回指定节点之后的下一个可接受节点。
    return treeWalker.nextNode();
  }
  throw new TypeError("direction 只能是 next 或 previous");
}

这段封装没有把 currentNode 保存成页面全局索引,因为 DOM 重渲染后,原来的节点对象可能已经不再是当前树中的节点。需要跨渲染恢复时,应保存业务标识,再在最新 DOM 中重新定位节点,最后执行同样的范围检查。

常见问题

设置 currentNode 后会自动跳到下一个节点吗?

不会。赋值只改变 TreeWalker 当前指向的节点;只有继续调用 nextNode()、previousNode() 或其他遍历方法时,游标才会按规则移动。

目标节点不在 root 子树中时会自动切换 root 吗?

不会。root 是创建 TreeWalker 时确定的边界。跨区域恢复应重新选择覆盖目标节点的根节点并创建新的 TreeWalker,而不是默默扩大当前遍历器的范围。

currentNode 赋值后为什么 nextNode 返回 null?

常见原因是目标节点后面没有符合 whatToShow 和 filter 的节点,或者目标节点已经位于当前方向的末端。可以改用 previousNode() 检查反向结果,但不要把 null 当成异常本身。

currentNode 能替代 querySelector 吗?

不能。querySelector 负责定位符合选择器的节点,currentNode 负责设置 TreeWalker 游标。常见组合是先定位,再校验范围,最后从指定位置继续遍历。

参考资料:https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker/currentNode、https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker。本文示例用于解释 API 关系,图示为原创静态说明图,不代表本机执行截图。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>