如何用纯JavaScript在HTML字符串中搜索并高亮指定单词?
纯JavaScript实现HTML字符串中指定单词的高亮
直接对HTML字符串做全局替换很容易踩坑——比如不小心把标签属性里的"test"也给替换了(比如<img alt="test-image">里的test就会被误改)。正确的做法是借助DOM API来区分文本节点和标签内容,只高亮真正的文本内容,下面是完整的实现方案:
实现思路
- 将HTML字符串转换为可操作的DOM节点,避免直接字符串操作的风险
- 递归遍历所有节点,仅处理文本节点(跳过标签、注释等节点)
- 对每个文本节点,匹配目标关键词,将匹配部分用带高亮类的
<span>包裹 - 最后将处理后的DOM节点转回HTML字符串
完整代码示例
function highlightTextInHtml(htmlStr, keyword) { // 创建临时DOM容器,用于解析HTML字符串 const tempDiv = document.createElement('div'); tempDiv.innerHTML = htmlStr; // 递归遍历节点的函数 function traverseNodes(node) { // 如果是文本节点,处理高亮 if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent; // 构建正则:不区分大小写,匹配完整单词(避免部分匹配,比如"testing"里的test) // 如果不需要完整单词匹配,可以去掉\b const regex = new RegExp(`\\b${keyword}\\b`, 'gi'); // 如果文本中没有匹配,直接跳过 if (!regex.test(text)) return; // 重置正则的lastIndex,避免多次匹配出问题 regex.lastIndex = 0; // 拆分文本为匹配和非匹配的片段 const fragments = text.split(regex); let match; const newNodes = []; // 遍历拆分后的片段,插入文本节点和高亮节点 fragments.forEach((fragment, index) => { // 添加非匹配的文本片段 if (fragment) { newNodes.push(document.createTextNode(fragment)); } // 获取下一个匹配项(因为split会把匹配项去掉,所以要补回来) match = regex.exec(text); if (match && index < fragments.length - 1) { // 创建高亮span标签,添加自定义类 const highlightSpan = document.createElement('span'); highlightSpan.className = 'highlight'; highlightSpan.textContent = match[0]; newNodes.push(highlightSpan); } }); // 替换原来的文本节点为新的节点集合 node.replaceWith(...newNodes); } // 如果是元素节点,递归遍历子节点 else if (node.nodeType === Node.ELEMENT_NODE) { // 把子节点转成数组再遍历,避免遍历过程中节点变化导致的问题 Array.from(node.childNodes).forEach(traverseNodes); } } // 开始遍历临时div的所有子节点 Array.from(tempDiv.childNodes).forEach(traverseNodes); // 返回处理后的HTML字符串 return tempDiv.innerHTML; } // 测试用例 const originalHtml = "<html> <body> <p>Test this test paragraph. Don't touch <span class='test-class'>this</span></p> </body> </html>"; // 注意:先把HTML实体转义成真实的HTML标签,否则tempDiv解析的是实体字符串而不是标签 const decodedHtml = originalHtml.replace(/</g, '<').replace(/>/g, '>'); const highlightedHtml = highlightTextInHtml(decodedHtml, 'test'); console.log(highlightedHtml);
关键细节说明
- 避免误改标签内容:通过DOM节点类型判断,只处理
TEXT_NODE,完全不会碰标签的属性或结构 - 正则匹配控制:使用
\b确保匹配完整单词(如果需要匹配部分文本,比如"testing"里的"test",可以去掉\b),gi标志实现全局、不区分大小写匹配 - 节点遍历安全:将子节点转为数组后再遍历,避免在修改节点时打乱遍历顺序
- HTML实体处理:如果输入的是带有
</>的实体字符串,需要先转义为真实的HTML标签,否则DOM容器会把它们当成普通文本解析
高亮样式补充
最后别忘了在CSS里定义高亮类的样式,比如:
.highlight { background-color: yellow; font-weight: bold; }
内容的提问来源于stack exchange,提问作者Milos
相关产品推荐
相关产品推荐

