最可靠的方式是直接用语义化且无障碍友好的标签包裹关键词,它原生支持、默认黄色背景、无需js即可渲染;需注意转义正则元字符、避免xss、禁止嵌套、慎用、框架中优先用vnode而非v-html、服务端高亮应统一转为、mark.js需显式开启diacritics和iframes选项。

直接用 <mark></mark> 标签包裹关键词最可靠
浏览器原生支持 <mark></mark>,语义正确、无障碍友好、无需 JS 就能渲染,且默认带黄色背景——这正是搜索高亮最需要的起点。别绕弯写 <span class="highlight"></span>,除非你明确放弃语义和可访问性。
常见错误是把整个搜索逻辑堆在 JS 里做字符串替换,却忘了给生成的标签加 <mark></mark>,结果样式靠 CSS 强撑,屏幕阅读器完全感知不到“这是匹配结果”。
- 只对**已知确切位置**的关键词使用:比如后端返回了高亮后的 HTML 字段(Elasticsearch 的
highlight结果),或前端搜索后精确知道子串起止索引 - 避免对用户输入未转义的内容直接插进
<mark></mark>:可能触发 XSS,务必先用textContent提取纯文本,再用innerHTML插入安全的<mark></mark>片段 -
<mark></mark>不能嵌套,重复高亮同一段文字会导致 DOM 结构异常
用正则替换时必须处理特殊字符和空格
用户搜 1+1=2 或 c++,不转义正则元字符会直接崩掉 replace()。空格多、大小写混、中英文标点穿插也会让匹配失败——这不是“搜不到”,而是“根本没匹配上”。
实操建议:把关键词转成安全正则字面量,再用 g 标志全局替换:
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[]\]/g, '\$&');
}
const safeKey = escapeRegExp(keyWord);
const pattern = new RegExp(`(${safeKey})`, 'gi');
const highlighted = text.replace(pattern, '<mark>$1</mark>');
- 别漏掉
i标志:用户不关心大小写,但正则默认区分 - 中文搜索慎用
(单词边界):汉字没有传统“词界”,wordBoundary: true在 mark.js 里反而会漏匹配 - 多个关键词用
|拼接时,每个都要单独escapeRegExp(),否则一个+就让整条正则失效
Vue/React 等框架里别直接拼 HTML 字符串
框架会拦截原始 HTML 渲染,v-html 或 dangerouslySetInnerHTML 是唯一出口,但也是风险口。更稳的方式是拆成文本节点 + <mark></mark> 节点组合。
比如 Vue3 中,不要这样写:
<div v-html="highlightText(item.content, keyword)"></div>
而应借助 computed 返回 VNode 数组:
computed: {
highlightedContent() {
const parts = this.content.split(new RegExp(`(${this.keyword})`, 'gi'));
return parts.map(part =>
part.toLowerCase() === this.keyword.toLowerCase()
? h('mark', part)
: h('span', part)
);
}
}
- 服务端返回的高亮 HTML(如 Elasticsearch 的
<em></em>包裹)需提前统一转成<mark></mark>,避免客户端再解析标签 - SSR 场景下,
v-html可能导致水合 mismatch,优先用响应式节点生成 - el-table 列中用
template#default时,必须用v-html或函数式渲染,{{ }}插值会自动转义
用 mark.js 时注意 iframe 和变音符号默认不生效
mark.js 看似开箱即用,但两个默认行为常被忽略:它不进 <iframe></iframe>,也不自动归一化带重音的字符(比如搜 cafe 匹配不了 café)。
调用时必须显式打开对应选项:
$('.content').mark(keyword, {
diacritics: true,
iframes: true,
separateWordSearch: false,
element: 'mark' // 强制用语义化标签,而非默认的 span
});
-
iframes: true后仍需确保 iframe 同源,跨域 iframe 内容无法操作 -
diacritics: true依赖插件内置映射表,对中文拼音、日文假名无效,仅适用于拉丁系变音字符 - 若页面含大量动态加载内容,需在内容插入 DOM 后再调用
mark(),不能只在初始加载时跑一次
真正难的不是“怎么高亮”,而是“怎么确保每次搜索都精准定位、不漏不重、不崩不卡”。从关键词转义、DOM 更新时机,到跨框架渲染和 SSR 水合,每个环节都可能悄悄吃掉高亮效果。留心这些点,比换十个库都管用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











