在前端开发中,对页面内容进行关键词高亮是一种常见的交互需求,广泛应用于搜索结果展示、文档预览、笔记应用等场景。本文汇总了多种实现方案,从原生 JavaScript 到现代 CSS API,再到成熟的第三方库,供开发者根据项目需求选用。
1. 原生 JavaScript 动态高亮
这是最灵活、可控性最高的方法,核心思路是遍历 DOM 树中的文本节点,将匹配的关键词用带有高亮样式的标签(如 <mark> 或 <span>)包裹起来。
基本步骤:
- 获取要搜索的容器元素。
- 使用
TreeWalker或递归遍历所有文本节点。 - 对每个文本节点的内容进行正则匹配(需转义正则特殊字符)。
- 将匹配部分拆分为新的节点,并用
<mark class="highlight">包裹。 - 将原文本节点替换为新的节点片段。
示例代码(简单版):
javascript
function highlightText(container, keyword) {
const regex = new RegExp(keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {
acceptNode: (node) => {
// 跳过隐藏元素和脚本/样式标签内的文本
if (node.parentElement.tagName === 'SCRIPT' || node.parentElement.tagName === 'STYLE')
return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT;
}
});
const nodesToReplace = [];
while (walker.nextNode()) {
const node = walker.currentNode;
if (regex.test(node.nodeValue)) {
nodesToReplace.push(node);
}
}
nodesToReplace.forEach(node => {
const span = document.createElement('span');
span.innerHTML = node.nodeValue.replace(regex, match => `<mark class="highlight">${match}</mark>`);
node.parentNode.replaceChild(span, node);
});
}
优缺点:
- ✅ 精准控制,可避免破坏 HTML 结构。
- ✅ 兼容性好(支持所有浏览器)。
- ❌ 实现稍复杂,需处理正则转义、跨文本节点匹配、性能优化等问题。
- ❌ 修改 DOM 会触发重绘,大量高亮时需考虑性能。
2. 利用 CSS Custom Highlight API
这是现代浏览器提供的新特性(Chrome 105+、Edge 105+、Firefox 预览版),允许通过 JavaScript 标记文本范围,然后通过 CSS 定义高亮样式,无需修改 DOM 结构。
实现方式:
- 使用
document.createRange()确定要高亮的范围。 - 创建
Highlight对象,将范围加入其中。 - 通过
CSS.highlights.set()注册高亮。 - 在 CSS 中使用
::highlight()伪元素设置样式。
示例代码:
javascript
// 假设已获取所有文本节点中的匹配位置
const highlight = new Highlight();
const ranges = []; // 收集所有匹配的 Range 对象
ranges.forEach(range => highlight.add(range));
CSS.highlights.set('search-result', highlight);
css
::highlight(search-result) {
background-color: yellow;
color: black;
}
优缺点:
- ✅ 不修改 DOM,性能优异,尤其适合大型文档。
- ✅ 样式与逻辑分离,易于维护。
- ❌ 浏览器支持度尚在提升(需检查目标用户群)。
- ❌ 需要自行计算文本范围(可借助
document.caretRangeFromPoint或遍历文本节点)。
3. 使用第三方库
如果不想重复造轮子,成熟的库可以快速集成,并提供额外功能(如忽略标点、变音符号匹配、多词高亮等)。
推荐库:
-
mark.js
专注文本高亮的轻量级库(~8kB gzip),支持动态内容、自定义元素、精确匹配、同义词等。javascriptnew Mark(document.querySelector('.content')).mark('keyword'); -
rangy
功能强大的跨浏览器选择和范围库,可配合其 CSS 类应用模块实现高亮。 -
textillate
结合 CSS3 动画的文本高亮库,适合动态效果。
优缺点:
- ✅ 开箱即用,节省开发时间。
- ✅ 通常已处理边界情况(如忽略脚本标签、跨节点匹配)。
- ❌ 引入额外依赖,可能增加打包体积。
4. 后端预高亮 + 前端展示
对于静态内容或 SEO 友好的场景,可以在服务端渲染时直接生成高亮标签。
实现方式:
- 后端(如 Node.js、Python)接收搜索词,在返回的 HTML 片段中用
<mark>包裹匹配内容。 - 前端直接渲染即可。
优缺点:
- ✅ 前端几乎零逻辑,渲染速度最快。
- ✅ 有利于搜索引擎收录(如果高亮内容有意义)。
- ❌ 不适用于动态搜索(每次搜索需请求后端)。
- ❌ 高亮逻辑与后端耦合,难以复用。
5. 基于框架的组件方案
在 React、Vue 等现代框架中,可以封装高亮组件,利用虚拟 DOM 或指令实现。
React 示例(函数组件):
jsx
function Highlight({ text, keyword }) {
if (!keyword) return text;
const regex = new RegExp(`(${keyword})`, 'gi');
const parts = text.split(regex);
return (
<>
{parts.map((part, i) =>
regex.test(part) ? <mark key={i}>{part}</mark> : part
)}
</>
);
}
使用方式:<Highlight text={content} keyword={searchTerm} />
Vue 自定义指令:
javascript
Vue.directive('highlight', {
update(el, binding) {
// 使用 mark.js 或原生方法处理 el 内的文本
}
});
优缺点:
- ✅ 与框架生态完美融合,适合组件化项目。
- ✅ 可复用,易于维护。
- ❌ 需处理框架特有的生命周期和渲染机制。
6. 纯 CSS 局限性方法
严格来说,纯 CSS 无法根据动态搜索词高亮文本,但以下两种方式可在特定场景使用:
:target伪类 :当 URL 片段标识(如#section)匹配元素 ID 时,可高亮该元素,但无法高亮内部任意文本。::target-text伪元素 (部分浏览器支持):配合scroll-to-text片段(如#:~:text=keyword)可高亮指定文本,但需要 URL 触发,不适用于实时搜索。
这些方法限制较多,不推荐用于通用搜索高亮。
关键注意事项
无论采用哪种方法,都应考虑以下问题:
-
忽略无效节点
跳过
<script>、<style>、<noscript>等标签,以及display: none或visibility: hidden的元素。 -
转义正则特殊字符
如果搜索词包含
* . ? + $ ^ [ ] { } ( ) | \等符号,需先用反斜杠转义。 -
跨文本节点匹配
关键词可能跨越多个相邻文本节点(如
<span>Hel</span><span>lo</span>),简单的节点级替换会遗漏。高级方案需要合并相邻文本节点或使用 Range API。 -
性能优化
- 对长文档使用
requestAnimationFrame分批处理。 - 使用
IntersectionObserver仅对可视区域进行高亮。 - 避免频繁替换大块 DOM,可考虑文档片段(DocumentFragment)。
- 对长文档使用
-
动态内容更新
如果页面内容通过 AJAX 加载或用户交互变化,需在更新后重新执行高亮。
-
可访问性
确保高亮颜色对比度足够,并考虑使用
<mark>标签(语义表示标记)而非<span>。 -
多词/模糊匹配
若需要同时高亮多个词或忽略变音符号,可借助正则捕获组或第三方库的扩展功能。
总结
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 原生 JavaScript | 需要完全控制,兼容老浏览器 | 灵活、兼容性好 | 实现复杂,需处理边界 |
| CSS Custom Highlight API | 现代浏览器,追求高性能 | 不修改 DOM,性能极佳 | 浏览器支持度有限 |
| 第三方库(如 mark.js) | 快速集成,无需重复造轮子 | 功能丰富,稳定可靠 | 增加依赖 |
| 后端预高亮 | 静态内容,SEO 优先 | 前端开销最小 | 不适用动态搜索 |
| 框架组件 | React/Vue 项目,组件化开发 | 生态融合,易复用 | 依赖框架,需处理渲染细节 |
根据项目具体情况选择最适合的方案:小型项目或老浏览器可用原生 JS;追求性能且用户群现代可尝试 CSS Highlight API;复杂需求推荐 mark.js;框架项目则可封装专用组件。无论哪种,都需注意处理边缘情况,保证用户体验和页面可访问性。