Interfejs CSS Custom Highlight API umożliwia stylizowanie dowolnych zakresów tekstu na stronie bez modyfikowania struktury DOM. Umożliwia to wyróżnianie wyników wyszukiwania, kolorowanie składni, wyświetlanie kursorów do wspólnej edycji oraz oznaczanie błędów ortograficznych i gramatycznych bez konieczności umieszczania tekstu w dodatkowych elementach lub manipulowania innerHTML.
Implementacja podstawowa
Aby wyróżnić zakresy tekstu, musisz zebrać docelowe węzły tekstowe, utworzyć obiekty Range i Highlight, zarejestrować je w HighlightRegistry, a następnie nadać im styl za pomocą pseudoelementu ::highlight().
1. Zbieranie węzłów tekstowych i tworzenie zakresów
Użyj TreeWalker, aby zebrać wszystkie węzły tekstowe w elemencie docelowym, a następnie utwórz obiekty Range wskazujące przesunięcia znaków, które chcesz wyróżnić.
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);
Zapisz listę węzłów tekstowych w pamięci podręcznej i odświeżaj ją tylko wtedy, gdy zawartość DOM ulegnie zmianie, ponieważ przechodzenie przez drzewo jest kosztowne.
2. Tworzenie filmu z najciekawszymi momentami na podstawie zakresów
Grupowanie co najmniej 1 obiektu Range w Highlight. Wiele zakresów, które mają ten sam styl, należy do jednego wyróżnienia.
const searchHighlight = new Highlight(...matchingRanges);
3. Zarejestruj wyróżnienie w rejestrze
Zarejestruj każdy element Highlight pod niestandardową nazwą, używając CSS.highlights, czyli HighlightRegistry podobnego do Map.
// 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);
Gdy kilka wyróżnień nakłada się na siebie, użyj właściwości priority, aby kontrolować kolejność ich nakładania. Wyróżnienia o wyższym priorytecie są wyświetlane na górze.
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. Styl: ::highlight()
Użyj pseudoelementu ::highlight() w CSS, aby nadać styl każdemu zarejestrowanemu wyróżnieniu według nazwy.
::highlight(search-results) {
background-color: #ffdd00;
color: black;
}
Wewnątrz ::highlight() działa tylko ograniczony zestaw właściwości CSS: color, background-color, text-decoration i ich długie formy, text-shadow, -webkit-text-stroke-color, -webkit-text-fill-color i -webkit-text-stroke-width. Właściwości takie jak background-image, font-size czy padding są ignorowane.
Ułatwienia dostępu
AVOID używania wyróżnień niestandardowych jako zamiennika semantycznych znaczników HTML.
Wyróżnienia niestandardowe służą wyłącznie do prezentacji i nie są widoczne w drzewie ułatwień dostępu. Jeśli zaznaczony tekst jest semantycznie powiązany z dokumentem (np. jest to fragment wybrany przez użytkownika), użyj elementu <mark>. Wyróżnienia niestandardowe należy stosować tylko w przypadku efektów tymczasowych i wyłącznie wizualnych, takich jak wyniki wyszukiwania czy kolorowanie składni.
Wyróżnienia nie powinny opierać się wyłącznie na kolorze, aby przekazać znaczenie. Jeśli podświetlenie wskazuje błąd, połącz je z innym wskaźnikiem wizualnym, takim jak text-decoration: wavy underline lub sąsiadująca etykieta tekstowa. Zadbaj o odpowiedni kontrast między tłem wyróżnienia a kolorem tekstu, aby spełnić wymagania WCAG 2.1 (co najmniej 4,5:1 w przypadku zwykłego tekstu).
Strategie zastępcze
W przypadku przeglądarek, które nie obsługują interfejsu CSS Custom Highlight API, należy zapewnić podstawowe funkcje, dzięki którym tekst będzie nadal czytelny, nawet bez wizualnego wyróżnienia niestandardowego.
Przed użyciem interfejsu API możesz sprawdzić, czy jest on obsługiwany:
if (CSS.highlights) {
// CSS Custom Highlight API is supported.
} else {
// Fallback: wrap matches in <mark> elements.
}
Jeśli wyróżnienie jest kluczowe dla wygody użytkownika, możesz użyć elementów <mark> do wyróżnienia dopasowanego tekstu. Zmienia to DOM, więc zachowaj ostrożność, aby nie usunąć detektorów zdarzeń i nie uszkodzić struktury dokumentu.
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);
}
}