You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何遍历全部节点并将terms数组中存在的匹配词包裹在span标签内

文本关键词匹配高亮实现方案

需求说明

遍历所有nodes文本节点,检查节点内容是否包含terms数组中的任意词条,匹配到的词汇自动用带自定义属性的span标签包裹,可用于后续术语提示等功能。

原代码存在的问题

  • 正则表达式的单词边界符\b在JS字符串中没有转义,实际被解析为退格符,导致匹配完全失效
  • 未处理词条重叠问题:如knowledge base包含base词条,短词条先替换会破坏长词条的匹配,长词条先替换后短词条又会匹配span标签内的文本,导致标签嵌套错乱
  • 遍历文本节点时重复修改父节点innerHTML,会导致TreeWalker生成的原始文本节点失效,出现重复处理、内容错乱的问题

修复后完整实现代码

JavaScript 代码

function getAllTextNodes(element) {
  let node;
  let nodes = [];
  // 仅获取不在script、style、已有glossary span内的文本节点,避免二次处理
  let walk = document.createTreeWalker(
    element,
    NodeFilter.SHOW_TEXT,
    {
      acceptNode: function(node) {
        const parentTag = node.parentNode.tagName.toLowerCase();
        if (['script', 'style', 'span'].includes(parentTag)) {
          return NodeFilter.FILTER_REJECT;
        }
        return NodeFilter.FILTER_ACCEPT;
      }
    },
    false
  );
  while (node = walk.nextNode()) nodes.push(node);
  return nodes;
}

const editor = document.getElementById('editor');
const terms = [
  {
    "id": 1,
    "definition": "知识库是公开发布的文档集合,通常包含常见问题解答、操作指南和故障排查内容",
    "expression": "knowledge base",
  },
  {
    "id": 2,
    "definition": "知识管理(KM)是组织创建、共享、使用和管理知识与信息的流程",
    "expression": "knowledge management",
  },
  {
    "id": 3,
    "definition": "这是测试内容",
    "expression": "base",
  },
  {
    "id": 4,
    "definition": "管理术语测试项",
    "expression": "management",
  },
  {
    "id": 5,
    "definition": "测试",
    "expression": "Knowledge",
  }
]

// 先按词条长度降序排序,优先匹配更长的词条,避免短词条破坏长词条匹配
const sortedTerms = terms.sort((a, b) => b.expression.length - a.expression.length);

getAllTextNodes(editor).forEach(node => {
  if (!node.parentNode) return;
  let text = node.textContent;
  let fragment = document.createDocumentFragment();
  let lastIndex = 0;

  // 构建匹配所有词条的正则,一次性匹配所有符合条件的内容
  const matchReg = new RegExp(
    sortedTerms.map(term => `\\b${term.expression}\\b`).join('|'),
    'ig'
  );

  let match;
  while ((match = matchReg.exec(text)) !== null) {
    // 先插入匹配位置之前的普通文本
    if (match.index > lastIndex) {
      fragment.appendChild(document.createTextNode(text.slice(lastIndex, match.index)));
    }
    // 找到匹配的词条信息
    const matchedTerm = sortedTerms.find(term => 
      term.expression.toLowerCase() === match[0].toLowerCase()
    );
    // 创建包裹用的span
    const span = document.createElement('span');
    span.className = 'hj-glossary-item';
    span.dataset.definition = matchedTerm.definition;
    span.dataset.id = matchedTerm.id;
    span.textContent = match[0];
    fragment.appendChild(span);
    lastIndex = matchReg.lastIndex;
  }

  // 插入剩余的普通文本
  if (lastIndex < text.length) {
    fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
  }

  // 替换原始文本节点
  node.parentNode.replaceChild(fragment, node);
});

HTML 代码

<div id="editor">
  <p>Management is the reason why knowledge management is important. Knowledge base is also important to base.</p>
  <p>Management is the reason why knowledge management is important. Knowledge base is also important to base.</p>
  <p>Management is the reason why knowledge management is important. Knowledge base is also important to base.</p>
</div>

优化点说明

  • 新增文本节点过滤逻辑,跳过已经是span、script、style标签内的内容,避免二次替换出现标签嵌套
  • 词条按长度排序后优先匹配长词,避免重叠词条匹配混乱
  • 用文档碎片+正则单次遍历的方式替换直接修改innerHTML,性能更高,也不会破坏原有DOM结构的其他事件绑定

内容的提问来源于stack exchange,提问作者Denis Omerovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 22:27:01