CSS 自定义高亮 API

CSS 自定义高亮 API 提供了一种机制,可以通过 JavaScript 创建范围,并使用 CSS 对范围进行样式化,从而对文档上的任意文本范围进行样式化。

概念和用法

对网页上的文本范围进行样式化非常有用。例如,文本编辑 Web 应用程序突出显示拼写或语法错误,代码编辑器突出显示语法错误。

CSS 自定义高亮 API 扩展了其他高亮伪元素的概念,例如 ::selection::spelling-error::grammar-error::target-text,它提供了一种方法来创建和样式化任意 Range 对象,而不是局限于浏览器定义的范围。

使用 CSS 自定义高亮 API,您可以以编程方式创建文本范围并突出显示它们,而不会影响页面中的 DOM 结构。

使用 CSS 自定义高亮 API 对网页上的文本范围进行样式化,需要四个步骤。

  1. 创建 Range 对象。
  2. 为这些范围创建 Highlight 对象。
  3. 使用 HighlightRegistry 注册高亮。
  4. 使用 ::highlight() 伪元素对高亮进行样式化。

创建范围

第一步是通过在 JavaScript 中创建 Range 对象来定义要样式化的文本范围。例如

js
const parentNode = document.getElementById("foo");

const range1 = new Range();
range1.setStart(parentNode, 10);
range1.setEnd(parentNode, 20);

const range2 = new Range();
range2.setStart(parentNode, 40);
range2.setEnd(parentNode, 60);

创建高亮

第二步是为您的文本范围实例化 Highlight 对象。

多个范围可以与一个高亮相关联。如果您想以相同的方式突出显示多段文本,则需要创建一个高亮并使用相应的范围对其进行初始化。

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

但您也可以根据需要创建任意数量的高亮。例如,如果您正在构建一个协作文本编辑器,其中每个用户都有不同的文本颜色,那么您就可以为每个用户创建一个高亮,如下面的代码片段所示。

js
const user1Highlight = new Highlight(user1Range1, user1Range2);
const user2Highlight = new Highlight(user2Range1, user2Range2, user2Range3);

每个高亮都可以进行不同的样式化。

注册高亮

创建高亮后,可以使用 HighlightRegistry(作为 CSS.highlights 提供)对其进行注册。

该注册表是一个类似 Map 的对象,用于使用自定义标识符注册高亮,如下所示。

js
CSS.highlights.set("user-1-highlight", user1Highlight);
CSS.highlights.set("user-2-highlight", user2Highlight);

在上面的代码片段中,user-1-highlightuser-2-highlight 字符串是自定义标识符,可以在 CSS 中使用它们来对注册的高亮应用样式。

您可以在注册表中注册任意数量的高亮,以及删除高亮和清除整个注册表。

js
// Remove a single highlight from the registry.
CSS.highlights.delete("user-1-highlight");

// Clear the registry.
CSS.highlights.clear();

样式化高亮

最后一步是对注册的高亮进行样式化。这是通过使用 ::highlight() 伪元素来完成的。例如,要对前面步骤中注册的 user-1-highlight 高亮进行样式化。

css
::highlight(user-1-highlight) {
  background-color: yellow;
  color: black;
}

接口

Highlight

此接口用于表示要在文档上进行样式化的范围集合。

HighlightRegistry

通过 CSS.highlights 访问,这个类似 Map 的对象用于使用自定义标识符注册高亮。

示例

突出显示搜索结果

此示例演示如何使用 CSS 自定义高亮 API 突出显示搜索结果。

HTML

下面的 HTML 代码片段定义了一个搜索字段和一篇包含几段文本的文章。

html
<label>Search within text <input id="query" type="text" /></label>
<article>
  <p>
    Maxime debitis hic, delectus perspiciatis laborum molestiae labore,
    deleniti, quam consequatur iure veniam alias voluptas nisi quo. Dolorem
    eaque alias, quo vel quas repudiandae architecto deserunt quidem, sapiente
    laudantium nulla.
  </p>
  <p>
    Maiores odit molestias, necessitatibus doloremque dolor illum reprehenderit
    provident nostrum laboriosam iste, tempore perferendis! Ab porro neque esse
    voluptas libero necessitatibus fugiat, ex, minus atque deserunt veniam
    molestiae tempora? Vitae.
  </p>
  <p>
    Dolorum facilis voluptate eaque eius similique ducimus dignissimos assumenda
    quos architecto. Doloremque deleniti non exercitationem rerum quam alias
    harum, nisi obcaecati corporis temporibus vero sapiente voluptatum est
    quibusdam id ipsa.
  </p>
</article>

JavaScript

JavaScript 用于监听搜索字段上的 input 事件。当事件触发时,代码在文章文本中定位输入文本的匹配项。然后,它为匹配项创建范围,并使用 CSS 自定义高亮 API 创建和注册 search-results 高亮对象。

js
const query = document.getElementById("query");
const article = document.querySelector("article");

// Find all text nodes in the article. We'll search within
// these text nodes.
const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const allTextNodes = [];
let currentNode = treeWalker.nextNode();
while (currentNode) {
  allTextNodes.push(currentNode);
  currentNode = treeWalker.nextNode();
}

// Listen to the input event to run the search.
query.addEventListener("input", () => {
  // If the CSS Custom Highlight API is not supported,
  // display a message and bail-out.
  if (!CSS.highlights) {
    article.textContent = "CSS Custom Highlight API not supported.";
    return;
  }

  // Clear the HighlightRegistry to remove the
  // previous search results.
  CSS.highlights.clear();

  // Clean-up the search query and bail-out if
  // if it's empty.
  const str = query.value.trim().toLowerCase();
  if (!str) {
    return;
  }

  // Iterate over all text nodes and find matches.
  const ranges = allTextNodes
    .map((el) => {
      return { el, text: el.textContent.toLowerCase() };
    })
    .map(({ text, el }) => {
      const indices = [];
      let startPos = 0;
      while (startPos < text.length) {
        const index = text.indexOf(str, startPos);
        if (index === -1) break;
        indices.push(index);
        startPos = index + str.length;
      }

      // Create a range object for each instance of
      // str we found in the text node.
      return indices.map((index) => {
        const range = new Range();
        range.setStart(el, index);
        range.setEnd(el, index + str.length);
        return range;
      });
    });

  // Create a Highlight object for the ranges.
  const searchResultsHighlight = new Highlight(...ranges.flat());

  // Register the Highlight object in the registry.
  CSS.highlights.set("search-results", searchResultsHighlight);
});

CSS

最后,::highlight() 伪元素在 CSS 中用于对高亮进行样式化。

css
::highlight(search-results) {
  background-color: #f06;
  color: white;
}

结果

结果如下所示。在搜索字段中键入文本以在文章中突出显示匹配项。

规范

规范
CSS 自定义高亮 API 模块级别 1

浏览器兼容性

api.Highlight

BCD 表格仅在浏览器中加载

api.HighlightRegistry

BCD 表格仅在浏览器中加载

css.selectors.highlight

BCD 表格仅在浏览器中加载

另请参见