mark是唯一语义正确、可访问且被读屏器识别的高亮方式,不可用span+class替代;它专用于标识与搜索等上下文强相关的文本,误用会损害可访问性、引发XSS风险并破坏DOM结构。

mark 是唯一语义正确、可访问且被读屏器识别的高亮方式,不能用 span + class 替代——它不是视觉装饰,而是告诉浏览器“这段文本与当前搜索强相关”。直接写 <mark>关键词</mark> 能生效,但真实项目中必须处理安全、样式和 DOM 结构三类问题。
为什么 innerHTML.replace() 一用就出事
这是最常见也最危险的写法。用户搜 <script>alert(1)</script> 或 cat[0],直接拼字符串会触发 XSS、DOM 解析中断或正则报错。
- 关键词含
&、"、等字符时,<code>innerHTML = text.replace(keyword, '<mark>$</mark>')会导致 HTML 解析失败,后续标签错位 - 关键词含正则元字符(如
.、*、[),new RegExp(keyword)直接抛错 - 原文是
<img src="logo.png">,搜logo后变成<img src="<mark>logo</mark>.png">,图片挂掉 - 根本没遍历文本节点,而是把整个 HTML 字符串当纯文本处理,破坏原有结构
怎么安全地动态插入 mark
核心是绕过字符串拼接,只操作纯文本内容,并用原生 DOM 方法组装节点。
- 用
document.createTreeWalker(element, NodeFilter.SHOW_TEXT)遍历所有文本节点,跳过空节点和仅含空白符的节点 - 对每个
textNode.textContent做匹配,先转义关键词:keyword.replace(/[.*+?^${}()|[]\]/g, '\$&') - 构造正则时加
gi标志确保全局不区分大小写:new RegExp(`(${escaped})`, 'gi') - 匹配后不用
replace()返回字符串,而是创建新节点:const mark = document.createElement('mark');→mark.textContent = matchedText;→textNode.replaceWith(mark); - 若需保留原始大小写(如页面是
React,用户搜react),用捕获组还原:text.replace(regex, (_, p1) => { const m = document.createElement('mark'); m.textContent = p1; return m; }),再用insertAdjacentElement插入
mark 的样式为什么必须手写
浏览器默认黄色背景在绝大多数生产环境里都不可靠——它不是 CSS 继承链的一部分,极易被覆盖或忽略。
立即学习“前端免费学习笔记(深入)”;
- Safari 阅读模式下直接剥离 UA 样式,
mark变成无背景纯文本 - Windows 高对比度模式强制重置未声明的
background-color,可能变透明或黑底白字 - 深色主题下默认
#ff0和深灰背景对比度低于 WCAG AA(要求 ≥4.5:1) - 邮件客户端(如 Outlook)根本不认 UA 默认值
- 必须显式声明:
mark { background-color: #ffeb3b; color: #212121; padding: 0.1em 0.25em; border-radius: 2px; } - 支持暗色模式时,补上:
@media (prefers-color-scheme: dark) { mark { background-color: #ffc107; color: #121212; } }
中文场景下要注意什么
中文没有单词边界,\b 失效,但也不能放任误匹配。
- 不依赖
\b,但要检查全角/半角一致性:用户搜JavaScript,页面里是javascript就匹配不到 - 多关键词时按长度从长到短排序,避免短词截断长词(如先匹配
JavaScript,再匹配Script) - 避免嵌套:
<mark><mark>Vue</mark></mark>会破坏语义,插入前先清理旧标记:element.querySelectorAll('mark').forEach(el => el.replaceWith(el.textContent)) - 别把
mark套在交互元素里(如按钮、链接内),读屏器会混淆上下文;也不该用于整段警告文案——那是strong或 CSS 类的事
真正麻烦的不是写几行高亮代码,而是每次插入都要判断:关键词是否已转义、文本节点是否被污染、样式是否在暗色模式下仍达标、读屏器是否能准确播报“高亮文本”。漏掉任意一环,用户看到的可能就是一片空白,或者一段无声无息的普通文字。



















