Start · Sprachen · JavaScript · Referenz · Range.surroundContents()

Range.surroundContents()

Methode

Verschiebt den Inhalt eines <code>Range</code> in einen neuen Elternknoten und fügt diesen an der Startposition des Bereichs ein.

seit JavaScript Baseline: alle Browser Kategorie: webapi

Signatur

Range.prototype.surroundContents(newParent)

Beschreibung

surroundContents() nimmt den Inhalt eines Range-Objekts und legt einen neuen Knoten darum, zum Beispiel ein <mark> oder <strong>. Intern macht die Methode ungefähr das Gleiche wie die Kombination aus extractContents(), insertNode() und appendChild(): Zuerst wird der Inhalt aus dem Dokument herausgelöst. Danach werden alle bisherigen Kindknoten von newParent entfernt, der herausgelöste Inhalt wird in newParent eingehängt, und newParent wird an der Startposition des Bereichs eingefügt. Zum Schluss umfasst der Range genau den neuen Elternknoten.

Typische Anwendungen sind das Hervorheben von Text, den Nutzer markiert haben, Suchtreffer-Markierungen, Anmerkungs-Werkzeuge und einfache Editor-Funktionen wie „Fett“ oder „Kursiv“ in contenteditable-Bereichen.

Wichtige Einschränkung: Der Range darf keinen Nicht-Text-Knoten nur teilweise enthalten. Start und Ende müssen also im selben Element liegen, oder alle betroffenen Elemente müssen komplett im Bereich enthalten sein. Ein Bereich, der mitten in einem <b> anfängt und außerhalb davon endet, löst einen InvalidStateError aus. Textknoten dürfen dagegen teilweise ausgewählt sein; sie werden an den Grenzen automatisch geteilt.

Parameter

Name Typ Default Beschreibung
$newParent Pflicht Node Der Knoten, der den Inhalt des Bereichs umschließen soll, meist ein neu erzeugtes Element. Er darf nicht vom Typ Document, DocumentType oder DocumentFragment sein. Vorhandene Kindknoten werden vorher entfernt.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined). Der Range umfasst danach den Knoten newParent.

Beispiele

Einen Textabschnitt mit <mark> hervorheben

// HTML: <p id="text">JavaScript ist vielseitig.</p>
const textNode = document.getElementById('text').firstChild;

const range = document.createRange();
range.setStart(textNode, 14); // "vielseitig" beginnt bei Index 14
range.setEnd(textNode, 24);

const mark = document.createElement('mark');
range.surroundContents(mark);

console.log(document.getElementById('text').innerHTML);
JavaScript ist <mark>vielseitig</mark>.

Nutzerauswahl fett formatieren (mit Fehlerbehandlung)

const makeBold = () => {
  const selection = window.getSelection();
  if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;

  const range = selection.getRangeAt(0);
  const strong = document.createElement('strong');

  try {
    range.surroundContents(strong);
  } catch (err) {
    if (err.name === 'InvalidStateError') {
      // Auswahl schneidet Elementgrenzen: robuster Ausweichweg
      strong.appendChild(range.extractContents());
      range.insertNode(strong);
    } else {
      throw err;
    }
  }
  selection.removeAllRanges();
};

document.getElementById('bold-btn').addEventListener('click', makeBold);

Alle Suchtreffer in einem Textknoten markieren

// HTML: <p id="doc">Katze, Hund, Katze, Maus</p>
const highlightAll = (element, term) => {
  let node = element.firstChild;
  while (node && node.nodeType === Node.TEXT_NODE) {
    const idx = node.data.indexOf(term);
    if (idx === -1) break;
    const range = document.createRange();
    range.setStart(node, idx);
    range.setEnd(node, idx + term.length);
    const mark = document.createElement('mark');
    range.surroundContents(mark);
    // Nach dem Teilen folgt der Rest-Text hinter <mark>
    node = mark.nextSibling;
  }
};

highlightAll(document.getElementById('doc'), 'Katze');
console.log(document.getElementById('doc').innerHTML);
<mark>Katze</mark>, Hund, <mark>Katze</mark>, Maus

// Wichtig · Fallstricke

  • InvalidStateError: Wird ausgelöst, wenn der Range einen Nicht-Text-Knoten nur teilweise enthält, zum Beispiel wenn eine Auswahl über mehrere Absätze geht oder mitten in einem <a> endet. Als Ausweichweg kann man extractContents() zusammen mit insertNode() verwenden. Das kann aber zusätzliche, aufgeteilte Elemente erzeugen.
  • InvalidNodeTypeError: Wird ausgelöst, wenn newParent ein Document, DocumentType oder DocumentFragment ist.
  • Vorhandene Kinder von newParent werden ohne Warnung entfernt. Übergib deshalb am besten immer ein frisch erzeugtes, leeres Element.
  • Event-Listener und Zustand der verschobenen Knoten bleiben erhalten, denn sie werden verschoben und nicht kopiert. Textknoten an den Grenzen werden aber geteilt. Referenzen auf den ursprünglichen Textknoten zeigen danach nur noch auf den ersten Teil.
  • In contenteditable-Bereichen gehen DOM-Änderungen über Range-Methoden nicht in den Rückgängig-Verlauf (Undo) des Browsers ein.
  • Browser-Kompatibilität: Die Methode wird von allen modernen Browsern unterstützt, auch von sehr alten Versionen (ab IE 9).