Mettre en surbrillance des plages de texte

Fonctionnalités Web : Points clés personnalisés

L'API CSS Custom Highlight vous permet de styliser des plages de texte arbitraires sur une page sans modifier la structure du DOM. Cela permet de mettre en surbrillance les résultats de recherche, de colorer la syntaxe, d'afficher les curseurs de modification collaborative ou de marquer les erreurs d'orthographe et de grammaire sans encapsuler le texte dans des éléments supplémentaires ni s'appuyer sur la manipulation du innerHTML.

Implémentation de base

Pour mettre en surbrillance des plages de texte, vous devez collecter les nœuds de texte cibles, créer des objets Range et Highlight, les enregistrer dans le HighlightRegistry, puis les styliser avec le pseudo-élément ::highlight().

1. Collecter les nœuds de texte et créer des plages

Utilisez un TreeWalker pour collecter tous les nœuds de texte de l'élément cible, puis créez des objets Range pointant vers les décalages de caractères que vous souhaitez mettre en surbrillance.

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

Mettez en cache la liste des nœuds de texte et ne la recréez que lorsque le contenu du DOM change réellement, car le parcours de l'arborescence est coûteux.

2. Créer un temps fort à partir des plages

Regroupez un ou plusieurs objets Range dans un Highlight. Plusieurs plages partageant le même style appartiennent à une seule mise en surbrillance.

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

3. Enregistrer la mise en surbrillance dans le registre

Enregistrez chaque Highlight sous un nom personnalisé à l'aide de CSS.highlights, qui est un HighlightRegistry de type 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);

Lorsque plusieurs mises en surbrillance se chevauchent, utilisez la propriété priority pour contrôler l'ordre d'empilement. Les mises en surbrillance de priorité plus élevée sont affichées au-dessus.

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. Style avec ::highlight()

Utilisez le pseudo-élément ::highlight() en CSS pour appliquer un style à chaque mise en surbrillance enregistrée par nom.

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

Seul un ensemble limité de propriétés CSS fonctionne dans ::highlight() : color, background-color, text-decoration et ses propriétés détaillées, text-shadow, -webkit-text-stroke-color, -webkit-text-fill-color et -webkit-text-stroke-width. Les propriétés telles que background-image, font-size ou padding sont ignorées.

Accessibilité

AVOID : utiliser des points clés personnalisés pour remplacer le balisage HTML sémantique.

Les mises en surbrillance personnalisées sont purement visuelles et ne sont pas exposées à l'arborescence d'accessibilité. Si le texte mis en surbrillance est sémantiquement pertinent pour le document (par exemple, un passage sélectionné par l'utilisateur), utilisez plutôt <mark>. Réservez les points clés personnalisés aux effets temporaires et purement visuels, comme les résultats de recherche ou la coloration syntaxique.

Les mises en surbrillance ne doivent pas s'appuyer uniquement sur la couleur pour transmettre du sens. Si une mise en surbrillance indique une erreur, associez-la à un autre indicateur visuel, tel que text-decoration: wavy underline ou un libellé de texte adjacent. Assurez-vous que le contraste entre l'arrière-plan de la mise en surbrillance et la couleur du texte est suffisant pour respecter les exigences WCAG 2.1 (au moins 4,5:1 pour le texte normal).

Stratégies de remplacement

Pour les navigateurs qui ne sont pas compatibles avec l'API CSS Custom Highlight, vous devez fournir une expérience de base fonctionnelle où le texte reste lisible, même sans la mise en surbrillance visuelle.

Vous pouvez détecter la compatibilité avant d'utiliser l'API :

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

Si la mise en surbrillance est essentielle pour l'expérience utilisateur, encapsulez le texte correspondant dans des éléments <mark>. Cela modifie le DOM. Veillez donc à conserver les écouteurs d'événements et à ne pas endommager la structure du document.

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