应使用document.createTreeWalker()遍历纯文本节点进行高亮,避免innerHTML.replace()破坏DOM结构、丢失事件和表单值,并用Unicode边界正则防止中文误匹配。

为什么直接 innerHTML.replace() 会破坏表格结构
表格里有 <input>、<select>、<button> 等交互元素,用 innerHTML.replace() 一执行,整个 <tr> 的 HTML 字符串被重写,所有已绑定事件丢失,表单值清空,甚至 <img src="a.jpg"> 可能被切进 <mark> 里变成 <img src="<mark>a</mark>.jpg"> —— 图片直接挂掉。
常见错误现象包括:
- 点击搜索后,表格里的下拉框无法再展开
- 用户刚输入的数值在
<input value="123">里瞬间变空 - 高亮完再点一次搜索,
<mark>嵌套出现:<mark><mark>关键词</mark></mark>
如何只操作表格里的纯文本节点
必须绕过 HTML 字符串,只处理真实的人类可读文本。核心是用 document.createTreeWalker() 遍历 NodeFilter.SHOW_TEXT 类型节点,并跳过非内容区域。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 从表格容器开始:const table = document.getElementById('myTable'); const walker = document.createTreeWalker(table, NodeFilter.SHOW_TEXT, null, false);
- 过滤掉无效节点:if (!node.textContent.trim() || node.parentElement?.tagName === 'SCRIPT' || node.parentElement?.tagName === 'STYLE' || node.parentElement?.tagName === 'TEXTAREA') continue;
- 对每个
node.textContent执行正则匹配,而不是整个innerHTML - 匹配后创建
mark元素:const mark = document.createElement('mark'); mark.textContent = matchedText; node.parentNode.replaceChild(mark, node);
中文关键词怎么避免“服务”匹配到“服务器”
中文无空格分隔,/服务/gi 会把“微服务架构”切开成“微服务架构”,语义断裂。不能靠简单正则解决,但可用 Unicode 边界模拟缓解。
推荐正则写法(需先转义关键词):
const escaped = keyword.replace(/[.*+?^${}()|[]\]/g, '\$&');
const reg = new RegExp(`(?<=[\s\u4e00-\u9fa5]|^)${escaped}(?=[\s\u4e00-\u9fa5]|$)`, 'gi');
说明:
-
(?<=...)是正向先行断言,不消耗字符,只检查左侧是否为汉字、空白或行首 -
(?=...)同理检查右侧边界 - 仍会漏掉“服务费”“服务商”等复合词,但比全局匹配可读性高得多
高亮后表格样式错乱或 mark 不显示
<mark> 标签本身没有可靠默认样式——Safari 阅读模式剥离它,Windows 高对比度模式重置背景色,深色主题下默认黄色 (#ff0) 完全不可见。
必须显式声明 CSS:
mark {
background-color: #ffeb3b;
color: #212121;
padding: 0.1em 0.3em;
border-radius: 3px;
}
@media (prefers-color-scheme: dark) {
mark {
background-color: #ffca28;
color: #121212;
}
}
注意:mark 是语义标签,不是视觉装饰,不能用 <span class="highlight"> 替代——读屏器依赖它识别搜索上下文,替换会损害可访问性。
真正容易被忽略的是:高亮后要调用 node.parentNode.normalize() 合并相邻文本节点,否则连续两次搜索同一词,会产生多个孤立 mark 节点,后续再匹配可能失效。



















