搜索结果高亮我要在代码里算字符位置塞 span,今天 CSS 自己会了——::highlight() 把这件事彻底原生化了

以前做搜索结果高亮,我要在代码里算字符位置,然后往 DOM 里塞 span——今天发现 CSS 自己会了,::highlight() 把这件事彻底原生化了。

搜索结果高亮、代码编辑器的语法高亮、协作文档的多人光标……这些场景你一定写过。传统做法是拿 JavaScript 找到要标记的文字,算好字符偏移,然后往 DOM 里插 <span>,最后给 span 挂 class。逻辑不复杂,但每次改文字内容你得重新算一遍;如果文字跨了多个节点,还得递归遍历 DOM 树慢慢拼。

CSS Custom Highlight API 把这件事彻底变了。

这个 API 怎么工作的

核心思想很直接:JavaScript 负责告诉浏览器「哪段文字要标记」,CSS 负责「怎么标记」,两者各做各的事,不动 DOM 结构。

用起来分四步:

第一步:创建 Range 对象。Range 是浏览器原生的文字范围 API,不依赖 DOM 树结构。比如我想高亮「浏览器」这三个字:

const textNode = document.querySelector("p").firstChild;
const range = new Range();
range.setStart(textNode, 5);  // 从第5个字符开始
range.setEnd(textNode, 8);    // 到第8个字符结束

字符位置怎么算?textNode.textContent.indexOf("浏览器") 就是,不用自己猜。

第二步:把 Range 包进 Highlight 对象。一个 Highlight 可以装多个 Range,也就是说你可以一次高亮所有搜索匹配项:

const highlight = new Highlight(range1, range2, range3);

第三步:注册到 CSS。CSS.highlights 是一个 Map,你给每个 Highlight 起个名字:

CSS.highlights.set("search-result", highlight);

这个名字是 CSS 和 JS 之间的桥梁。

第四步:CSS 中用 ::highlight() 引用这个名字,给高亮区域定义样式:

::highlight(search-result) {
  background-color: #ffeb3b;
  color: #000;
  text-decoration: underline;
}

效果立刻生效,不需要重新渲染任何东西。

重叠高亮的处理方式

你可能同时需要「搜索结果高亮」和「用户当前选中区域高亮」两个效果。传统做法两者会打架,要么后写的盖掉前写的,要么得一整套优先级逻辑。

CSS Custom Highlight API 用 CSS 优先级本身来决定谁盖谁。在 ::highlight(search-result)::highlight(user-selection) 两个样式都定义了的情况下,更具体的选择器、或者后定义的规则会胜出——你熟悉的那套 cascade 规则直接拿来用就行。

支持的样式有限制

::highlight()::selection 一样,只能设置一部分文本相关属性:color、background-color、text-decoration、text-shadow、-webkit-text-stroke、text-fill。这是有意为之——高亮区域不能改变布局,不能影响文字流,不能让你在搜索结果里插入换行。如果放开这些限制,整个页面的排版就乱套了。

注册时机要讲究

Highlight 注册之后,对应的 Range 就和那个具体位置绑定了。如果你之后修改了 DOM——比如用户输入了新文字,或者动态渲染了列表——原来的 Range 就会失效,继续用会报错。最稳妥的做法是:每次内容更新,重新走一遍四步流程。Range 对象很轻量,频繁重建不是问题。

哪些浏览器支持

Baseline 2026(2026 年 3 月起),Chrome、Edge、Safari、Firefox 最新版均已支持。caniuse 上全球支持率在八成左右,生产可用。

下一步做什么

如果你现在在做搜索结果高亮、多人协作文档、或者任何需要标记文字的场景,把你现有的 span 注入逻辑抽出来,替换成上面的四步。定义好哪些场景需要什么颜色方案,然后在内容渲染前调用一次注册逻辑就够了。这个改动通常不超过 30 行代码。

评论区

0 条评论

登录后可评论。