本篇依然来自于我们的 《前端周刊》 项目!
由团队成员 0bipinnata0 翻译,这位佬有技术追求、翻译风格精准细腻,还擅长挖掘原文背后的技术细节~
欢迎大家 进群 同该佬深度交流😁 以及持续追踪全球最新前端资讯!!

最近 CSS Custom Highlight API 引起了我的注意,因为 Firefox 最近开始支持它(Firefox 140,2025年6月),这使得所有主流浏览器都支持了这个 API。通过它,你可以对通过 JavaScript 中的 Range()
类获取的文本应用(某些)样式。我本来想说是你选择的文本,但这里实际上并没有涉及真正的普通选择器,这对于像我这样的 CSS 开发者来说是相当不寻常的。
我认为这里需要一个基本的文字说明,因为当我第一次开始研究它时,这样的说明肯定会对我有帮助:
-
你需要一个
textNode
(例如document.querySelector("p").firstChild
) -
然后你需要一个
Range()
,在其上执行setStart
和setEnd
,这意味着范围现在在这两个整数之间。 -
然后你在该 Range 上调用
CSS.highlights.set()
,给它一个名称。 -
然后你在 CSS 中使用
::highlight()
,传入你刚才使用的名称。
如果我们在页面上有一个 <p>
文本,整个过程看起来是这样的:
JavaScript
const WORD_TO_HIGHLIGHT = "wisdom";
const NAME_OF_HIGHLIGHT = "our-highlight";
const textNode = document.querySelector("p").firstChild;
const textContent = textNode.textContent;
const startIndex = textContent.indexOf(WORD_TO_HIGHLIGHT);
const endIndex = startIndex + WORD_TO_HIGHLIGHT.length;
const range = new Range();
range.setStart(textNode, startIndex);
range.setEnd(textNode, endIndex);
const highlight = new Highlight(range);
CSS.highlights.set(NAME_OF_HIGHLIGHT, highlight);
在开发者工具中看到这个效果很有趣,单词 "wisdom" 明显应用了自定义 CSS 样式,但在该单词周围没有你通常认为应用这些样式所必需的元素。

这很可能就是浏览器本身在需要仅对文本的某些部分应用样式时所做的事情,比如当你使用浏览器内置的查找功能时。

这是演示:
为什么这很有用?
-
能够在完全不需要 操作 DOM 的情况下定位和样式化文本是很有趣的。有时,DOM API 被批评为缓慢,所以能够避免这种情况可能是有利的,特别是如果你需要大量这样做的话。
-
添加和删除
<span>
元素,除了可能"缓慢"之外,还会影响 DOM 结构,从而可能影响其他处理 DOM 的 CSS 和 JavaScript。 -
DOM 复杂度可能是网页性能的一个问题。过多的 DOM 节点,重新计算可能非常"昂贵",页面上的用户体验可能会受到影响,比如动画和滚动变慢。
这是一个只有 17 个更改文件的 GitHub PR 页面。该页面已经有超过 4,500 个 span 元素,用于诸如代码差异着色和语法高亮等功能。这已经相当重了,而且肯定会变得更糟。

我确信这个 API 存在的原因还有很多,但这些只是我立即想到的几个原因。
做更多事情(搜索示例)
创建一个 new Highlight()
可以接受多个 Range。这意味着 CSS 中的单个 ::highlight()
可以应用于许多文本范围。如果我们在页面上构建自己的搜索功能,这将很有用。如果搜索是你正在构建的 Web 应用程序的关键功能,我可以很容易地想象为它构建自己的 UI,而不是依赖内置的浏览器功能。
这次,让我们让要在文本中查找的单词来自用户:
HTML
<label>
Search the text below
<input type="search" value="oven" id="searchTerm">
</label>
然后我们监听变化:
JavaScript
window.searchTerm.addEventListener("input", (e) => {
doSearch(e.target.value.toLowerCase());
});
注意我们将输入的值传递给一个函数,并在传递时将其转换为小写,因为搜索在不区分大小写时通常最有用。
我们的 doSearch
函数然后将接受该搜索词并在所有文本上运行正则表达式:
JavaScript
const regex = new RegExp(searchTerm, "gi");
我们需要的是一个包含所有找到的文本实例索引的数组。这是一段有点冗长的代码,但就是这样:
JavaScript
const indexes = [...theTextContent.matchAll(new RegExp(searchTerm, 'gi'))].map(a => a.index);
有了这个索引数组,我们可以循环遍历它们创建 Range,然后将所有 Range 发送到新的 Highlight。
JavaScript
const arrayOfRanges = [];
indexes.forEach(matchIndex => {
// 从索引值创建一个 "Range"。
const searchRange = new Range();
searchRange.setStart(par, matchIndex);
searchRange.setEnd(par, matchIndex + searchTerm.length);
arrayOfRanges.push(searchRange);
})
const ourHighlight = new Highlight(...arrayOfRanges);
CSS.highlights.set("search-results", ourHighlight);
总的来说,它创建了一个功能完整的搜索体验:
用于语法高亮
感觉语法高亮代码是这个 API 的一个很好的用例。André Ruffert 已经采用了这个想法并付诸实践,制作了一个 [<syntax-highlight> Web Component](https://andreruffert.github.io/syntax-highlight-element/)
,它使用 Lea Verou 的 Prism.js 来解析代码,但然后不像开箱即用的 Prism 那样应用 <span>
,而是使用这个自定义高亮 API。
示例:
我认为这很棒,但值得注意的是,这个 API 只能 在客户端使用。对于语法高亮这样的功能,这可能意味着在看到代码和语法高亮"生效"之间会有延迟。我承认在可能的情况下,我更喜欢服务器端渲染的语法高亮。这意味着如果你可以从服务器提供一堆像这样的 <span>
(并且不会严重影响性能或可访问性),那可能会更好。
我也承认我仍然对内置语法高亮的字体有些着迷,这感觉像是字体厂商可以进入的未开发领域。