
高亮文本的主流方式
高亮文本乍一看简单,只需要改一下背景为黄色,但其实很复杂。比如下面这个例子,高亮的部分横跨多个元素并包含元素下面的部分文本。
下面从几种常见的文本高亮实现方式讲起,结合优缺点和实际使用场景,梳理一下主流技术方案。
CSS Highlight API —— 现代且高效的文本高亮方式
这是目前实现文本高亮的最佳选择,简单易用且性能优越。唯一的缺点是浏览器需要较新版本支持,不过在2025年的当下所有主流浏览器也都支持了。
Highlight 对象
首先你需要为每个高亮区域创建一个 Range
对象。 Range 是一个古已有之的API,代表DOM树上一段连续的区域,如果你不清楚的话就自己补补课。
const range = new Range();
range.setStart(someNode, startOffset);
range.setEnd(someNode, endOffset);
然后添加到 Highlight
对象。因为高亮可以有多个不连续的区域,所以一个 Highlight
对象也可以有若干个 Range。
const highlight = new Highlight(range1, range2, ..., rangeN);
它是一个类似 Set 的对象,所以可以用 add
, delete
等方法修改包含的 Range。
CSS.highlights
然后将 Highlight 对象添加到 CSS.highlights
里。它是一个类似Map的对象,添加删除成员的方法和Map一样,比如 get
, set
, delete
等,遍历成员的方法也是一样的。
就和 Map一样,在添加 Highlight 的时候需要给他一个 key,当作这个 highlight 的名字了。这个名字有很大的用处,下面会看到,我们用这个名字来设置高亮的样式,比如是绿色背景。所以同一个 Highlight 对象的高亮样式是相同的。如果你想要再用蓝色高亮另一段文本,那么要新建一个 highlight。
CSS.highlights.set("my-highlight", highlight);
定义伪元素 ::highlight
接下来就轮到css样式登场了,用 ::highlight(高亮的名字)
伪元素就可以定义高亮的样式啦。
::highlight(my-highlight) {
background-color: yellow;
color: black;
}
注意:只能使用少数几种 CSS 属性,例如
background-color
、color
、cursor
等,详见规范。
动态修改高亮范围
CSS.highlights
就像一个 Map,它有类似的方法来更新 Highlight
CSS.highlights.clear()
set(highlightName, Highlight)
delete(highlightName)
Highlight
对象就像 Set,有类似的方法来更新 Range
Highlight.add(range)
delete(range)
修改文本元素的样式
通过遍历 DOM 找到目标文本节点,直接修改其背景样式来实现高亮。
- 需要对 DOM 进行修改,浏览器会重新布局,性能开销较大。
- 实现跨元素高亮(即高亮范围跨越多个元素)较为复杂。
- 适合高亮内容相对固定,且范围较小的场景。
覆盖额外图层渲染高亮,不改动文本 DOM
创建一个透明的叠加层,计算目标文本的屏幕位置,然后在叠加层绘制半透明的高亮。
定位高亮区域
- 使用
Element.getClientRects()
或getBoundingClientRect()
获取文本在页面上的坐标。 - 处理多行文本时,每行对应一个矩形区域,需分别绘制高亮块。
- 窗口尺寸变动或字体大小变化时需要重新计算高亮位置,性能可能受影响。
高亮的绘制方式
DIV 作为叠加层
使用绝对定位的父容器和半透明背景的子元素覆盖文本。 例如 Monaco 编辑器就是用此方案实现同词高亮。
<div class="editor" style="position: relative;">
<div class="overlay" style="position: absolute; height: 0;">
<div
class="highlight"
style="top:0; left:57px; width:23px; background-color: rgba(173,214,255,0.15);"
></div>
<!-- 添加更多 highlight 的区域 -->
</div>
<div class="content">
<!-- 文本内容 -->
</div>
</div>
- overlay 的 position 是 absolute,同时保证父元素不是
position: static
,这样 absolute 才能生效。这可以让内部的高亮元素定位到任意位置。 - 想要在文本上方,只需要在HTML里将 overlay 放在文本容器之前。overlay 的高度为 0,避免影响下方文本的交互。
- 内部添加元素,
background-color
设为半透明的高亮颜色,定位到高亮文本的位置,设置 width - Monaco 编辑器(VSCode就是基于它的)用的这种办法。见官方演示,选中某个关键字后,相同的关键字都会高亮。而查看 HTML 会发现,这些关键字并没有改变样式,而是在编辑器界面上有一个 Overlay,里面绘制了高亮的半透明颜色(见
class="view-overlays"
的 div)
SVG 作为叠加层
- overlay 设置为和页面长宽一样
- 内部添加
<rect>
,然后到高亮文本的位置绘制 - foliate-js 用的这个办法。见 overlayer.js。
利用 Selection API 和 ::selection 伪元素 —— 浏览器默认的文本选择高亮
浏览器本身会高亮选中的文本,所以控制选区就可以变相实现高亮。样式可通过 CSS 的 ::selection
伪元素自定义。
const range = new Range();
range.setStart(parentNode, startOffset);
range.setEnd(parentNode, endOffset);
// 设置选区
const selection = document.getSelection();
selection.removeAllRanges();
selection.addRange(range);
::selection {
background-color: #c3c2c2;
color: white;
}
这个方法比较取巧,很简单而且浏览器支持广泛,但有一个重大缺陷:因为同时只能有一个选区,编程选区和用户实际选区会互相覆盖冲突。所以只适用于满足以下条件的场景
- 用户不能选择文本
- 只有一段连续的高亮文本
- 需要支持老旧的浏览器
- 你想偷懒,不想用其他方法