必须为显式定义css样式并安全插入dom,否则在暗色模式、高对比度、邮件客户端等场景下会失效或引发xss。

浏览器默认用黄色背景渲染 <mark></mark>,但这个“默认效果”在真实项目中基本不可靠——不加 CSS 就上线,大概率在暗色模式、高对比度设置、打印页面或邮件客户端里完全看不见。
为什么mark标签不能只靠浏览器默认样式
默认背景色(通常是 #ff0)不是 CSS 继承链中的一环,它被系统级样式覆盖的概率极高:
- Windows 高对比度模式会强制重置所有未显式声明的
background-color,<mark></mark>可能变成纯黑底白字,甚至透明 - Safari 在阅读模式下会剥离 UA 样式,
<mark></mark>黄底直接消失 - Outlook 等邮件客户端解析 HTML 时,根本不认 UA 默认值
- 深色主题下,
#ff0和深灰背景对比度低于 1.5:1,远低于 WCAG AA 要求的 4.5:1
必须写死关键样式:mark { background-color: #ffeb3b; color: #212121; padding: 0.1em 0.3em; border-radius: 3px; }。如果支持暗色模式,还得补上 @media (prefers-color-scheme: dark) 规则。
innerHTML.replace() 是最危险的高亮写法
直接对容器的 innerHTML 做字符串替换,等于同时打开 XSS、DOM 解析中断、标签错位三扇门:
- 用户搜
<script>alert(1)</script>,拼进字符串后执行脚本 - 关键词含
&或",导致后续 HTML 解析失败,页面结构塌陷 - 原文是
<img src="logo.png">,搜logo后变成<img src="<mark>logo</mark>.png">,图片挂掉
安全做法是绕过字符串拼接:用 document.createTreeWalker(element, NodeFilter.SHOW_TEXT) 遍历纯文本节点,在每个 node.textContent 上做匹配,再用 document.createElement('mark') 插入,不碰 innerHTML。
正则匹配关键词必须转义两层
用户输入的搜索词既是 HTML 内容,又是正则模式,必须分别处理:
- HTML 转义:把
&→&、→ <code><,防止注入和解析错误 - 正则转义:把
.、*、[、?等元字符前加\,否则new RegExp(keyword)直接报错或误匹配 - 英文场景建议加单词边界
\b,避免cat匹配到education;中文一般不加,但要注意全角/半角一致性
别信 String.prototype.replace() 的字符串插值形式(如 '<mark>$&</mark>'),优先用函数回调:text.replace(regex, (match) => { const m = document.createElement('mark'); m.textContent = match; return m; }),再用 insertAdjacentElement 插入。
document.querySelector('mark') 找不到?大概率不是语法问题
返回 null 的真实原因往往和大小写无关(HTML 标签名不区分大小写):
-
<mark></mark>是 JS 动态插入的,但插入后 DOM 没触发 layout(比如在requestIdleCallback中执行) - 其他逻辑清空了容器,比如某个 cleanup 函数又执行了
element.innerHTML = '' - 目标在 Shadow DOM 内,得用
shadowRoot.querySelector('mark') - 容器设了
contenteditable,Safari 会自动剥离<mark></mark>节点
验证是否真插入成功,直接查 element.querySelectorAll('mark').length,比 querySelector 更可靠;若为 0,说明 DOM 操作根本没生效,而不是选择器写错了。
真正难的不是让文字变黄,而是让“高亮”这件事在任意设备、任意辅助技术、任意渲染上下文中都保持语义正确、视觉可辨、结构安全——这三点缺一不可。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











