mark 是唯一能语义化表达“上下文相关高亮”的html元素,仅适用于搜索关键词、文档引用术语、代码修改部分等场景;禁用 innerhtml 拼接、必须显式声明样式、禁止嵌套或包裹交互元素。

mark 标签不是“加个黄色背景”的快捷方式,它是唯一能向浏览器、搜索引擎和读屏器明确传达“此处为当前上下文相关高亮”的语义化元素。用错地方或忽略可访问性细节,反而会让搜索高亮失效甚至损害体验。
什么时候必须用 mark,而不是 span + class?
只在以下场景中,mark 是不可替代的:
- 搜索结果页中动态包裹用户输入的关键词(如用户搜“React”,页面中所有 “React” 文本需被语义化标记)
- 文档引用片段中临时标出与当前段落强相关的术语(如教程里“请检查
package.json中的 dependencies 字段”) - 代码示例中突出显示被修改/新增的行内部分(如
const value = <mark>42</mark>;),且该高亮需被辅助技术识别为“上下文标记”而非“强调重要性”
反例:按钮文案写 <mark>立即购买</mark>、整段警告文字套 mark、或在无上下文的独立 banner 里用它——这些都该用 strong 或 CSS 类。
直接 innerHTML 拼接 mark 很危险
常见错误是这样写:
element.innerHTML = text.replace(keyword, '<mark>$&</mark>');
这会触发 XSS 风险(如果 keyword 来自 URL 或用户输入),且破坏 DOM 结构(比如 keyword 含 或 <code>>)。正确做法是:
- 先用
text.split()切分原始文本,避免正则替换污染 - 对每个切片调用安全转义函数(如
escapeHtml()),再对匹配项单独包裹mark - 最终用
document.createTextNode()和document.createElement('mark')组装节点,而非字符串拼接
简化的安全高亮函数核心逻辑:
function highlight(text, keyword) {<br> const escapedKeyword = escapeHtml(keyword);<br> const parts = text.split(new RegExp(`(${escapedKeyword})`, 'gi'));<br> return parts.map(part =><br> part === keyword || part.toLowerCase() === keyword.toLowerCase()<br> ? `<mark>${escapeHtml(part)}</mark>`<br> : escapeHtml(part)<br> ).join('');<br>}
mark 的样式必须显式声明
浏览器默认样式(浅黄背景 + 黑字)在以下情况会彻底消失:
- 用户启用阅读模式(Safari / Edge)
- 邮件客户端渲染 HTML(如 Outlook)
- 禁用 UA 样式的 CSS 重置(如某些设计系统)
所以生产环境务必写死关键样式:
mark {<br> background-color: #ffeb3b;<br> color: #212121;<br> padding: 0.1em 0.25em;<br> border-radius: 2px;<br> /* 不要只写 background: none —— 它可能漏掉 background-image */<br>}
额外注意:深色模式下默认黄色极难读,建议配合 @media (prefers-color-scheme: dark) 调整背景与文字色。
别让 mark 嵌套或包裹交互元素
mark 是纯内容标记,不是容器。以下写法会导致焦点顺序错乱、读屏器重复播报或语义丢失:
<mark><button>点我</button></mark>-
<mark>详见 <mark>第3节</mark></mark>(嵌套) -
<mark><img src="icon.svg" alt="警告"></mark>(非文本内容)
如果需要高亮一段含按钮的文字,应把 mark 放在按钮外侧文本中,按钮本身保持原样;若整个区块需视觉统一,改用 CSS 类控制背景,而非语义错误地滥用 mark。










