Back

使用 CSS Custom Highlight API 为文本添加样式

使用 CSS Custom Highlight API 为文本添加样式

CSS Custom Highlight API 解决了一个基本问题:如何在不使用包装元素污染 DOM 的情况下以编程方式高亮文本范围。无论您是在构建搜索界面、文本编辑器还是教育应用程序,这个 API 都提供了比传统的 <mark> 或基于 span 的方法更清洁、性能更优的解决方案。

核心要点

  • CSS Custom Highlight API 可以在不操作 DOM 的情况下实现文本高亮
  • 高亮功能通过 Range 对象、Highlight 对象和 CSS.highlights 注册表来实现
  • 由于不会发生布局重新计算,性能显著提升
  • 可以同时高亮多个非连续的范围

CSS Custom Highlight API 的独特之处

传统的文本高亮需要修改 DOM 结构。使用 <mark> 元素或 span 意味着插入节点、触发布局重新计算,以及在高亮跨越多个元素时管理复杂的 HTML。CSS Custom Highlight API 通过使用 JavaScript ranges 和 ::highlight 伪元素来消除这些问题,保持您的 DOM 结构清洁。

该 API 由三个核心组件组成:

  • Range 对象:定义文本边界
  • Highlight 对象:对范围进行分组
  • CSS.highlights 注册表:将高亮与样式连接

创建和样式化文本范围

构建您的第一个高亮

首先创建一个 Range 对象来定义要高亮的文本:

const range = new Range();
const textNode = document.querySelector('p').firstChild;
range.setStart(textNode, 0);
range.setEnd(textNode, 20);

接下来,创建一个 Highlight 对象并注册它:

const highlight = new Highlight(range);
CSS.highlights.set('my-highlight', highlight);

最后,使用 ::highlight 伪元素为其添加样式:

::highlight(my-highlight) {
  background-color: yellow;
  color: black;
}

实现搜索结果高亮

以下是一个在文档中高亮搜索结果的实际示例:

function highlightSearchResults(searchTerm) {
  // 清除现有高亮
  CSS.highlights.delete('search-results');
  
  const walker = document.createTreeWalker(
    document.body,
    NodeFilter.SHOW_TEXT
  );
  
  const ranges = [];
  let node;
  
  while (node = walker.nextNode()) {
    const text = node.textContent;
    const regex = new RegExp(searchTerm, 'gi');
    let match;
    
    while (match = regex.exec(text)) {
      const range = new Range();
      range.setStart(node, match.index);
      range.setEnd(node, match.index + searchTerm.length);
      ranges.push(range);
    }
  }
  
  if (ranges.length > 0) {
    const searchHighlight = new Highlight(...ranges);
    CSS.highlights.set('search-results', searchHighlight);
  }
}

高级模式:多范围高亮

当处理多个非连续文本范围时,真正的威力才会显现。单个 Highlight 对象可以包含多个 Range 对象:

// 高亮多个独立的段落
const range1 = new Range();
range1.selectNodeContents(document.querySelector('#intro'));

const range2 = new Range();
range2.selectNodeContents(document.querySelector('#conclusion'));

const highlight = new Highlight(range1, range2);
CSS.highlights.set('important-sections', highlight);

您还可以同时管理多个高亮组:

// 具有不同样式的不同高亮类型
CSS.highlights.set('errors', new Highlight(...errorRanges));
CSS.highlights.set('warnings', new Highlight(...warningRanges));
CSS.highlights.set('info', new Highlight(...infoRanges));
::highlight(errors) {
  background-color: #fee;
  text-decoration: wavy underline red;
}

::highlight(warnings) {
  background-color: #ffa;
}

::highlight(info) {
  background-color: #e6f3ff;
}

性能优势和浏览器支持

CSS Custom Highlight API 在性能关键场景中表现出色。与 DOM 操作不同,高亮不会触发布局重新计算或因额外元素产生内存开销。浏览器的渲染引擎在绘制层处理高亮,使更新速度极快。

截至 2025 年,Chrome、Edge 和 Safari 支持该 API,Firefox 支持正在开发中。对于较旧的浏览器,请实现功能检测回退:

if ('CSS' in window && 'highlights' in CSS) {
  // 使用 Custom Highlight API
  const highlight = new Highlight(range);
  CSS.highlights.set('fallback-safe', highlight);
} else {
  // 回退到传统方法
  const span = document.createElement('span');
  span.className = 'highlight';
  range.surroundContents(span);
}

实际实现技巧

在生产环境中实现文本高亮时:

  1. 清理未使用的高亮以防止内存泄漏:

    CSS.highlights.delete('old-highlight');
  2. 处理动态内容,在 DOM 变化时更新范围:

    highlight.add(newRange);
    highlight.delete(oldRange);
  3. 使用有意义的高亮名称来描述其用途(例如 ‘search-results’、‘spell-check’、‘user-annotations’)

结论

CSS Custom Highlight API 改变了我们在 Web 上处理文本高亮的方式。通过将高亮逻辑与 DOM 结构分离,它提供了比传统方法更好的性能、更清洁的代码和更大的灵活性。开始在您的下一个项目中尝试高亮文本范围——您的用户(和您的 DOM)会感谢您的。

常见问题

是的,该 API 可以高亮跨越多个元素的文本。Range 对象可以在一个元素中开始,在另一个元素中结束,这使其非常适合高亮跨越段落或 span 边界的短语。

高亮仍然附着在其原始 Range 对象上。如果 DOM 发生变化,您需要手动更新或重新创建范围。该 API 不会自动跟踪 DOM 变化,因此需要为动态内容实现自己的观察者模式。

多个高亮可以重叠同一文本范围。浏览器会应用所有匹配的高亮样式,后定义的样式具有优先权。您可以通过 CSS 属性(如 background-color 和 opacity)控制视觉层叠。

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay