实现关键词高亮的核心是:遍历文本节点,拆分「前缀+关键词+后缀」并用<mark>包裹,需避免破坏DOM、重复标记、误处理表单及已标记内容。

实现搜索关键词在页面中的高亮显示,核心是:找到所有包含关键词的文本节点,将其拆分为「前缀 + 关键词 + 后缀」三部分,再用 <mark>(或自定义样式标签)包裹关键词,最后替换原节点。关键在于不破坏 DOM 结构、避免重复高亮、跳过已标记内容和表单元素。
1. 基础思路:递归遍历文本节点
不能直接用 innerHTML 全局替换(会丢失事件绑定、破坏表单状态),应逐个访问文本节点(nodeType === 3),过滤掉空格/换行等纯空白节点,并排除 <script>、<style>、<input>、<textarea> 等非展示区域。
- 使用
document.createTreeWalker()或递归childNodes遍历更可靠 - 对每个文本节点,用
textContent检查是否包含关键词(注意大小写处理) - 匹配时用正则(带
g标志)确保找全,但需转义特殊字符(如.、*、(等)
2. 安全替换:避免嵌套和重复标记
高亮后生成的 <mark> 是元素节点,若再次运行高亮函数,可能把 <mark>关键词</mark> 中的“关键词”又包一层,导致 HTML 错乱。因此必须跳过已有 HTML 标签内的文本:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 检查父节点是否为
<mark>、<span class="highlight">等标记容器 - 或统一约定:只处理原始文本节点(
parentNode.nodeType === 1且parentNode.children.length === 0) - 替换时用
document.createTextNode()和document.createElement('mark')构造新节点,再调用replaceChild()
3. 实用代码片段(含转义与忽略逻辑)
以下是一个轻量可用的函数示例:
立即学习“Java免费学习笔记(深入)”;
function highlightText(keyword, root = document.body) {
if (!keyword.trim()) return;
// 转义正则特殊字符
const escaped = keyword.replace(/[.*+?^${}()|[]\]/g, '\$&');
const regex = new RegExp(escaped, 'gi');
const walker = document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
{
acceptNode(node) {
const parent = node.parentNode;
// 跳过 script/style/input/textarea/mark 及其后代
if (
['SCRIPT', 'STYLE', 'INPUT', 'TEXTAREA', 'SELECT', 'OPTION'].includes(parent?.nodeName) ||
parent?.closest('script, style, input, textarea, select, option, mark')
) return NodeFilter.FILTER_REJECT;
return node.textContent.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
}
);
let node;
while (node = walker.nextNode()) {
const text = node.textContent;
const matches = [...text.matchAll(regex)];
if (matches.length === 0) continue;
const fragment = document.createDocumentFragment();
let lastIndex = 0;
matches.forEach(match => {
const index = match.index;
// 添加前一段文本
if (index > lastIndex) {
fragment.appendChild(document.createTextNode(text.slice(lastIndex, index)));
}
// 添加高亮标签
const mark = document.createElement('mark');
mark.textContent = match[0];
mark.style.backgroundColor = '#ffeb3b';
mark.style.color = '#212121';
fragment.appendChild(mark);
lastIndex = index + match[0].length;
});
// 添加末尾剩余文本
if (lastIndex < text.length) {
fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
}
node.parentNode.replaceChild(fragment, node);
}
}
4. 使用建议与注意事项
- 调用前可先清除旧高亮:
document.querySelectorAll('mark').forEach(el => el.replaceWith(el.textContent)) - 支持中文、英文、数字混合关键词,无需额外编码处理
- 如需支持模糊匹配或分词高亮(如搜索“北京”也匹配“北京市”),需引入分词库或扩展正则逻辑
- 性能敏感场景(如长文章实时搜索),可加防抖、限制匹配数量或只高亮首 N 处

















