Textbereiche hervorheben

Mit der CSS Custom Highlight API können Sie beliebige Textbereiche auf einer Seite gestalten, ohne die DOM-Struktur zu ändern. So können Suchergebnisse hervorgehoben, Syntax farblich gekennzeichnet, Cursor für die gemeinsame Bearbeitung angezeigt oder Rechtschreib- und Grammatikfehler markiert werden, ohne dass Text in zusätzliche Elemente eingeschlossen oder das innerHTML manipuliert werden muss.

Grundlegende Implementierung

Wenn Sie Textbereiche hervorheben möchten, müssen Sie die Zieltextknoten erfassen, Range- und Highlight-Objekte erstellen, sie in der HighlightRegistry registrieren und dann mit dem Pseudoelement ::highlight() formatieren.

1. Textknoten erfassen und Bereiche erstellen

Verwenden Sie einen TreeWalker, um alle Textknoten im Zielelement zu erfassen, und erstellen Sie dann Range-Objekte, die auf die Zeichen-Offsets verweisen, die Sie hervorheben möchten.

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);

Speichern Sie die Textknotenliste im Cache und erstellen Sie sie nur neu, wenn sich der DOM-Inhalt tatsächlich ändert, da das Durchlaufen des Baums aufwendig ist.

2. Highlight aus den Bereichen erstellen

Fassen Sie ein oder mehrere Range-Objekte in einem Highlight zusammen. Mehrere Bereiche mit demselben Stil gehören zu einer einzigen Hervorhebung.

const searchHighlight = new Highlight(...matchingRanges);

3. Highlight in der Registry registrieren

Registrieren Sie jede Highlight unter einem benutzerdefinierten Namen mit CSS.highlights, einer Map-ähnlichen HighlightRegistry.

// 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);

Wenn sich mehrere Hervorhebungen überschneiden, können Sie die Stapelreihenfolge mit der Property priority steuern. Hervorhebungen mit höherer Priorität werden darüber angezeigt.

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. Stil mit ::highlight()

Verwenden Sie das Pseudoelement ::highlight() in CSS, um jede registrierte Hervorhebung nach Name zu gestalten.

::highlight(search-results) {
  background-color: #ffdd00;
  color: black;
}

In ::highlight() funktionieren nur eine begrenzte Anzahl von CSS-Properties: color, background-color, text-decoration und die zugehörigen Langschreibweisen, text-shadow, -webkit-text-stroke-color, -webkit-text-fill-color und -webkit-text-stroke-width. Attribute wie background-image, font-size oder padding werden ignoriert.

Bedienungshilfen

AVOID: Benutzerdefinierte Hervorhebungen als Ersatz für semantisches HTML verwenden.

Benutzerdefinierte Hervorhebungen dienen nur der Darstellung und werden nicht im Baum für Barrierefreiheit im Internet angezeigt. Wenn der hervorgehobene Text semantisch relevant für das Dokument ist (z.B. ein vom Nutzer ausgewählter Abschnitt), verwenden Sie stattdessen <mark>. Benutzerdefinierte Hervorhebungen sollten nur für vorübergehende, rein visuelle Effekte wie Suchergebnisse oder Syntaxfärbung verwendet werden.

Hervorhebungen sollten nicht nur durch Farben gekennzeichnet werden. Wenn eine Hervorhebung auf einen Fehler hinweist, kombinieren Sie sie mit einem anderen visuellen Indikator wie text-decoration: wavy underline oder einem angrenzenden Textlabel. Achten Sie auf einen ausreichenden Kontrast zwischen dem Hintergrund der Hervorhebung und der Textfarbe, um die Anforderungen der WCAG 2.1 zu erfüllen (mindestens 4,5:1 für normalen Text).

Fallback-Strategien

Für Browser, die die CSS Custom Highlight API nicht unterstützen, sollten Sie eine funktionale Basisversion bereitstellen, in der der Text auch ohne die visuelle Hervorhebung lesbar ist.

Sie können die Unterstützung prüfen, bevor Sie die API verwenden:

if (CSS.highlights) {
  // CSS Custom Highlight API is supported.
} else {
  // Fallback: wrap matches in <mark> elements.
}

Wenn die Hervorhebung für die Nutzererfahrung entscheidend ist, solltest du den übereinstimmenden Text in <mark>-Elemente einschließen. Dadurch wird das DOM geändert. Achten Sie also darauf, dass Event-Listener erhalten bleiben und die Dokumentstruktur nicht beschädigt wird.

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);
  }
}