innerHTML.replace()必然失败,因其将HTML当纯字符串处理,导致表单值清空、事件监听器丢失、XSS漏洞、DOM结构非法;正则易误匹配标签/属性/实体,跨节点替换引发DOM撕裂,且无法安全处理用户输入中的&等转义字符。

innerHTML.replace() 不能用,一用就崩——表单值清空、事件监听器丢失、XSS 漏洞、DOM 结构非法,不是“可能出错”,是“必然失败”。
为什么必须避开 innerHTML.replace()
浏览器把 innerHTML 当纯字符串处理,但里面混着标签、属性、脚本和用户输入值。搜 a,可能把 <input value="a"> 里的 a 也包进去;搜 [ 或 .,正则直接报 Invalid regular expression;更糟的是,原始文本跨多个节点(比如 <strong>React</strong> Native),正则一替换,DOM 就撕裂,Range.surroundContents() 立刻抛 InvalidNodeTypeError。
- 表单控件(如
<input value="已填内容">)高亮后变纯文本,值彻底丢失 -
<script></script>或<textarea></textarea>内容被误匹配,执行脚本或污染结构 - 用户输入含
&、、<code>"等字符,未转义就拼进字符串 → DOM 解析中断
只操作文本节点:用 TreeWalker 安全遍历
核心原则:只动 Node.TEXT_NODE(即 nodeType === 3),跳过所有元素、脚本、样式、textarea 和空文本节点。
- 用
document.createTreeWalker(root, NodeFilter.SHOW_TEXT)创建遍历器,它天然过滤非文本节点 - 循环中加守卫:
if (!node.textContent.trim() || ['SCRIPT', 'STYLE', 'TEXTAREA'].includes(node.parentElement?.tagName)) continue - 对每个有效节点,用
node.nodeValue做匹配(不是innerHTML) - 每次替换后立即调用
node.parentNode.normalize(),合并相邻文本节点,防重复高亮
关键词要双重转义,否则必出事
用户输入的关键词不是“拿来就能用”的字符串。它要过两关:
立即学习“前端免费学习笔记(深入)”;
-
HTML 转义:防止
&、"、'、`、、<code>=、/破坏 DOM,例如escapeHtml(keyword)→&、" -
正则转义:防止
.、[、?、$等元字符让new RegExp()报错,例如keyword.replace(/[.*+?^${}()|[\]]/g, '\$&') - 多关键词时,必须按长度 降序排序(
keywords.sort((a, b) => b.length - a.length)),否则#tagAgain会被先拆成#tag+Again,嵌套错乱
必须用 mark,且样式不能省
<mark></mark> 是唯一语义正确、读屏器明确播报、搜索引擎可提取的高亮方式。但它在 Windows 高对比度模式、Outlook 邮件客户端、CSS 重置环境下默认失效——没显式 background-color 和 color,等于没写。
-
background-color和color必须同时声明,缺一不可 - 加
padding和border-radius,原生mark紧贴文字,边缘生硬易误读 - 深色模式下默认黄色不可读,必须写
@media (prefers-color-scheme: dark) { mark { background-color: #ffcc00; color: #111; } } - 禁止嵌套
<mark><mark>xxx</mark></mark>,服务端已渲染mark时,前端 JS 绝不能再二次包裹
动态高亮真正难的不是“怎么加标签”,而是“怎么不破坏已有结构、不引入漏洞、不丢数据、不伤可访问性”。每一步绕开的坑,都是线上事故的前兆。



















