CSS Custom Highlight 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);

每個高亮都可以有不同的樣式。

註冊高亮

建立高亮後,使用可用的 CSS.highlights HighlightRegistry 來註冊它們。

登錄檔是一個類似 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) => ({ 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

最後,在 CSS 中使用 ::highlight() 偽元素來為高亮著色。

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

結果

結果如下所示。在搜尋欄位中輸入文字,即可高亮文章中的匹配項。

規範

規範
CSS 自定義高亮 API 模組級別 1

瀏覽器相容性

api.Highlight

api.HighlightRegistry

css.selectors.highlight

另見