安全高亮必须只操作文本节点:用TreeWalker遍历NodeFilter.SHOW_TEXT节点→跳过空节点→关键词双重转义→创建mark元素→replaceChild插入;禁用innerHTML.replace以防DOM破坏、XSS和样式失效。

点击按钮后只高亮指定容器内的文本,不破坏原有结构
直接改 innerHTML 是最常见也最危险的做法:表单输入值丢失、事件监听器消失、img 或 code 标签被撕开、XSS 漏洞敞开。必须绕过字符串拼接,只动纯文本节点。
正确路径是:document.createTreeWalker(element, NodeFilter.SHOW_TEXT) 获取所有文本节点 → 跳过空节点(node.textContent.trim() === '')→ 对每个节点内容做正则匹配 → 创建 mark 元素插入 DOM。
- 别用
element.innerText.replace(),它会丢掉换行、空格和原始格式 - 匹配前必须双重转义:HTML 实体转义(
&→&)+ 正则元字符转义(.→.) - 替换后用
node.parentNode.replaceChild(mark, node),不是拼字符串再赋值给innerHTML
按钮触发时如何安全处理用户输入的关键词
用户搜 [a-z] 或 <script>alert(1)</script>,不转义就会中断脚本或执行恶意代码。不能把搜索词直接塞进 new RegExp(keyword, 'gi')。
正则转义函数必须包含: keyword.replace(/[.*+?^${}()|[]\]/g, '\$&');HTML 转义要覆盖 &、"、'、`、=、/ 这六个字符。
立即学习“前端免费学习笔记(深入)”;
- 中文关键词需加
u标志支持 Unicode:new RegExp(pattern, 'giu') - 避免用
String.prototype.replace()的字符串替换形式(如'<mark>$&</mark>'),优先用函数回调:str.replace(regex, (match) => { ... }) - 多关键词同时高亮时,先按长度降序排序,否则短词会嵌套进长词里,导致语义断裂
高亮样式在深色模式、高对比度下失效怎么办
浏览器默认黄色背景在 Windows 高对比度模式下会被强制设为纯黑,Outlook 邮件客户端完全忽略 UA 样式,CSS 重置规则(如 * { background: transparent })会清空默认背景。不写 CSS 就上线,等于放弃可访问性。
至少声明这三项:background-color、color、padding;border-radius 是刚需,原生 mark 紧贴文字边缘生硬。
- 深色模式适配必须显式写:
@media (prefers-color-scheme: dark) { mark { background-color: #ffca28; color: #121212; } } - 别写
background: none—— 它不清除background-image,应写background-color: transparent - 色觉障碍用户可能无法识别黄底,建议搭配 padding 和 border-radius 提升视觉辨识度
为什么用 Range API 比正则匹配更可靠
正则匹配 /abc/g 会扫全页所有 “abc” 子串,不管它是否在同一个文本节点里,也不管它是否被用户真正选中。而 window.getSelection() + Range.surroundContents() 能精准包裹用户拖选或按钮指定的连续文本片段,保留原始 DOM 结构和语义边界。
适用于需要“所见即所得”高亮的场景,比如点击按钮高亮某段预设文案,或用户手动选中后点击按钮标记 —— 它天然规避了跨标签、换行、空格、标点误切等问题。
- 调用前先
range.cloneContents()备份,清除高亮时可还原 - 注意 IE 不支持
surroundContents(),需 fallback 到文本节点遍历 - 若目标区域含富文本(如
strong、em),surroundContents()仍能保持嵌套结构完整
真实项目里最易被忽略的不是怎么高亮,而是怎么安全地“取消高亮”——没保存原始文本或没克隆 Range,清除操作就变成不可逆的 DOM 破坏。



















