Signatur
Beschreibung
Ein Range-Objekt beschreibt einen Bereich im DOM über zwei Randpunkte: einen Start (startContainer + startOffset) und ein Ende (endContainer + endOffset). Ist der Container ein Textknoten, zählt der Offset Zeichen (genauer: UTF-16-Codeeinheiten), sonst zählt er Kindknoten. So lassen sich Bereiche genau angeben, die mitten in einem Wort anfangen und mehrere Elemente überspannen.
Einen Range erzeugst du mit document.createRange() oder new Range(). Die Grenzen setzt du mit setStart(), setEnd(), selectNode() oder selectNodeContents(). Zu den wichtigsten Methoden gehören extractContents(), cloneContents(), deleteContents(), insertNode(), surroundContents(), getBoundingClientRect(), getClientRects(), compareBoundaryPoints() und createContextualFragment().
Typische Einsatzfälle sind Texthervorhebungen, Rich-Text-Editoren, das programmgesteuerte Setzen der Benutzerauswahl (zusammen mit Selection), das Bestimmen der Pixelposition von Text (z. B. für Tooltips am Cursor) sowie das Erzeugen von DOM-Fragmenten aus HTML-Strings.
Ein Range ist live: Änderst du das DOM, passen sich die Randpunkte automatisch an. Wenn du das nicht willst, nimm StaticRange.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $(Konstruktor) new Range() | — | Der Konstruktor nimmt keine Argumente. Der neue Range ist zusammengefallen (collapsed ist true) und liegt am Anfang des globalen document, also bei Container document und Offset 0. |
|
| $setStart(node, offset) | Node, number | Setzt den Startpunkt. offset zählt Zeichen bei Text-, Kommentar- und CDATA-Knoten und Kindknoten bei allen anderen Knoten. |
|
| $setEnd(node, offset) | Node, number | Setzt den Endpunkt. Liegt das Ende vor dem Start, fällt der Range auf das neue Ende zusammen. | |
| $collapse(toStart) | boolean | false | Lässt den Range auf einen Randpunkt zusammenfallen: true für den Start, false für das Ende. |
Rückgabewert
document.createRange() liefern ein neues Range-Objekt. Die Methoden liefern je nach Aufgabe z. B. ein DocumentFragment (extractContents(), cloneContents()), ein DOMRect (getBoundingClientRect()) oder undefined.Beispiele
Wortteil in einem Absatz hervorheben
// HTML: <p id="intro">Hallo Welt, schön dich zu sehen</p>
const textNode = document.getElementById('intro').firstChild;
const range = new Range();
range.setStart(textNode, 6); // vor „Welt“
range.setEnd(textNode, 10); // nach „Welt“
const mark = document.createElement('mark');
range.surroundContents(mark);
console.log(document.getElementById('intro').innerHTML);
Elementinhalt per Code auswählen (z. B. für einen Kopieren-Button)
const selectElementText = (el) => {
const range = document.createRange();
range.selectNodeContents(el);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
return selection.toString();
};
const code = document.querySelector('pre code');
console.log(selectElementText(code));
Pixelposition eines Textabschnitts für einen Tooltip ermitteln
const para = document.querySelector('p');
const range = document.createRange();
range.setStart(para.firstChild, 0);
range.setEnd(para.firstChild, 5);
const rect = range.getBoundingClientRect();
const tip = document.createElement('div');
tip.textContent = 'Hinweis';
tip.style.position = 'fixed';
tip.style.left = `${rect.left}px`;
tip.style.top = `${rect.bottom + 4}px`;
document.body.append(tip);
console.log(Math.round(rect.width) > 0);
Knoten verschieben und HTML als Fragment einfügen
const list = document.querySelector('ul');
const range = document.createRange();
// Alle <li> aus der Liste entfernen und als Fragment zwischenspeichern
range.selectNodeContents(list);
const items = range.extractContents();
console.log(list.children.length, items.childNodes.length > 0);
// Neues Markup im Kontext der Liste parsen und einfügen
const fragment = range.createContextualFragment('<li>Neu</li>');
range.insertNode(fragment);
console.log(list.innerHTML);
// Wichtig · Fallstricke
surroundContents()wirft einenInvalidStateError, wenn der Range einen Nicht-Textknoten nur teilweise einschließt, zum Beispiel wenn er mitten in einem<b>anfängt und außerhalb davon endet. Robuster istextractContents(), dann das Fragment in das Wrapper-Element einfügen und dieses mitinsertNode()einsetzen.- Offsets: In Textknoten zählen UTF-16-Codeeinheiten. Emojis und andere Zeichen außerhalb der BMP belegen 2 Einheiten, deshalb kann ein falscher Offset ein Ersatzpaar (Surrogate Pair) zerschneiden. Ein Offset außerhalb des gültigen Bereichs löst einen
IndexSizeErroraus. - Sicherheit:
createContextualFragment()parst HTML, und anders als beiinnerHTMLwerden darin enthaltene<script>-Elemente beim Einfügen ausgeführt. Übergib nie ungeprüfte Benutzereingaben (XSS-Gefahr). Bereinige sie z. B. mit DOMPurify oder nutze Trusted Types bzw. eine strenge CSP. - Live-Verhalten: Viele Ranges, die über längere Zeit bestehen bleiben, kosten bei häufigen DOM-Änderungen Leistung. Für einmalige Bereichsangaben ist
StaticRangebesser geeignet. - Kompatibilität: Range wird von allen modernen Browsern unterstützt.
new Range()gibt es erst seit etwa 2017 (der alte IE hat es nicht),document.createRange()dagegen überall. In Node.js steht Range nur über DOM-Implementierungen wie jsdom zur Verfügung. - Mehrere Ranges pro
Selectionunterstützt praktisch nur Firefox. Andere Browser verwenden nur den ersten Range.