CSS Custom Highlight API 可讓您設定網頁上任意文字範圍的樣式,而不必修改 DOM 結構。這樣一來,您就能醒目顯示搜尋結果、為語法上色、顯示協作編輯游標,或標示拼字和文法錯誤,不必將文字包裝在額外元素中,或依賴 innerHTML 操控。
核心實作
如要醒目顯示文字範圍,您必須收集目標文字節點、建立 Range 和 Highlight 物件、在 HighlightRegistry 中註冊這些物件,然後使用 ::highlight() 虛擬元素設定樣式。
1. 收集文字節點並建立範圍
使用 TreeWalker 收集目標元素中的所有文字節點,然後建立 Range 物件,指向要醒目顯示的字元偏移。
const article = document.querySelector("article");
// MANDATORY: Use TreeWalker to collect text nodes — do not manipulate innerHTML.
const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const allTextNodes = [];
let currentNode = treeWalker.nextNode();
while (currentNode) {
allTextNodes.push(currentNode);
currentNode = treeWalker.nextNode();
}
// MANDATORY: Set range start/end on text nodes, not element nodes.
const range = new Range();
range.setStart(textNode, matchStartIndex);
range.setEnd(textNode, matchEndIndex);
快取文字節點清單,並僅在 DOM 內容實際變更時重建,因為遍歷樹狀結構的成本很高。
2. 根據範圍建立精華片段
將一或多個 Range 物件分組為 Highlight。樣式相同的多個範圍屬於同一個醒目顯示。
const searchHighlight = new Highlight(...matchingRanges);
3. 在登錄檔中註冊醒目顯示
使用 CSS.highlights (類似 Map 的 HighlightRegistry),以自訂名稱註冊每個 Highlight。
// MANDATORY: Clear previous highlights before registering new ones
// to avoid stale ranges persisting on the page.
CSS.highlights.clear();
CSS.highlights.set("search-results", searchHighlight);
如果有多個醒目顯示重疊,請使用 priority 屬性控制堆疊順序。優先順序較高的醒目顯示會疊在上方。
const primary = new Highlight(...primaryRanges);
primary.priority = 1;
const secondary = new Highlight(...secondaryRanges);
secondary.priority = 0; // painted first (behind primary)
CSS.highlights.set("primary", primary);
CSS.highlights.set("secondary", secondary);
4. 使用 ::highlight() 設定樣式
在 CSS 中使用 ::highlight() 虛擬元素,依名稱設定每個註冊醒目顯示的樣式。
::highlight(search-results) {
background-color: #ffdd00;
color: black;
}
::highlight() 內僅支援一組有限的 CSS 屬性:color、background-color、text-decoration 及其長寫、text-shadow、-webkit-text-stroke-color、-webkit-text-fill-color 和 -webkit-text-stroke-width。系統會忽略 background-image、font-size 或 padding 等屬性。
無障礙設定
AVOID:使用自訂重點取代語意式 HTML。
自訂重點僅用於呈現,不會向無障礙樹狀結構公開。如果醒目顯示的文字與文件語意相關 (例如使用者選取的段落),請改用 <mark>。請將自訂重點保留給暫時性、僅限視覺的效果,例如搜尋結果或語法著色。
醒目顯示不應只依賴顏色來傳達意義。如果醒目顯示代表錯誤,請搭配其他視覺指標,例如 text-decoration: wavy underline 或相鄰的文字標籤。醒目顯示背景和文字顏色之間必須有足夠的對比度,符合 WCAG 2.1 規定 (一般文字至少 4.5:1)。
備用策略
如果瀏覽器不支援 CSS Custom Highlight API,請提供功能性基本體驗,確保文字即使沒有視覺醒目顯示,仍可清楚辨識。
您可以在使用 API 前偵測是否支援:
if (CSS.highlights) {
// CSS Custom Highlight API is supported.
} else {
// Fallback: wrap matches in <mark> elements.
}
如果醒目顯示對使用者體驗至關重要,請改為將相符文字包裝在 <mark> 元素中。這會修改 DOM,因此請務必保留事件監聽器,並避免破壞文件結構。
if (!CSS.highlights) {
// Walk text nodes and wrap matches in <mark>, preserving structure.
const walker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const nodes = [];
for (let n = walker.nextNode(); n; n = walker.nextNode()) nodes.push(n);
const term = searchTerm.toLowerCase();
for (const textNode of nodes) {
const text = textNode.textContent;
let pos = text.toLowerCase().indexOf(term);
if (pos === -1) continue;
const frag = document.createDocumentFragment();
let last = 0;
while (pos !== -1) {
frag.append(text.slice(last, pos));
const mark = document.createElement("mark");
// textContent assignment avoids HTML injection.
mark.textContent = text.slice(pos, pos + term.length);
frag.append(mark);
last = pos + term.length;
pos = text.toLowerCase().indexOf(term, last);
}
frag.append(text.slice(last));
textNode.replaceWith(frag);
}
}