Signatur
Beschreibung
Range.getClientRects() ermittelt die Layout-Rechtecke aller Teile eines Bereichs im Dokument. Anders als getBoundingClientRect(), das ein einziges umschließendes Rechteck liefert, bekommst du hier ein Rechteck pro Zeilenfragment bzw. pro Box. Erstreckt sich eine Textauswahl über drei Zeilen, erhältst du also mindestens drei Rechtecke.
Das ist besonders nützlich für eigene Hervorhebungen (Markierungen, Suchtreffer, Kommentar-Anker), für die Positionierung von Tooltips oder Toolbars an einer Textauswahl, für die Positionsbestimmung des Text-Cursors (bei einem zusammengeklappten Range) sowie zum Zählen der Zeilen, über die sich ein Textstück erstreckt.
Alle Koordinaten beziehen sich auf den Viewport (wie bei Element.getBoundingClientRect()). Für absolute Positionen im Dokument musst du window.scrollX bzw. window.scrollY addieren. Die Werte sind Momentaufnahmen und aktualisieren sich nicht, wenn gescrollt wird oder sich das Layout ändert.
Für Elemente, die vollständig im Range liegen, werden deren Rahmenrechtecke (wie von Element.getClientRects()) einbezogen; bei Textknoten, die nur teilweise enthalten sind, nur die Rechtecke des tatsächlich ausgewählten Textteils.
Rückgabewert
DOMRect-Objekten (Eigenschaften x, y, width, height, top, right, bottom, left) in Viewport-Koordinaten. Kann leer sein, wenn der Range keinen darstellbaren Inhalt umfasst.Beispiele
Rechtecke einer Textauswahl ausgeben
// Beim Loslassen der Maus die Zeilen der aktuellen Auswahl auflisten
document.addEventListener('mouseup', () => {
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
const range = selection.getRangeAt(0);
const rects = Array.from(range.getClientRects());
console.log(`Auswahl belegt ${rects.length} Rechteck(e)`);
rects.forEach((r, i) => {
console.log(`#${i}: left=${Math.round(r.left)}, top=${Math.round(r.top)}, width=${Math.round(r.width)}`);
});
});
Eigene Hervorhebung über mehrzeiligen Text legen
// Legt halbtransparente Overlays über jedes Zeilenfragment eines Range
const highlightRange = (range) => {
const overlays = [];
for (const rect of range.getClientRects()) {
const mark = document.createElement('div');
mark.className = 'custom-highlight';
Object.assign(mark.style, {
position: 'absolute',
left: `${rect.left + window.scrollX}px`,
top: `${rect.top + window.scrollY}px`,
width: `${rect.width}px`,
height: `${rect.height}px`,
background: 'rgba(255, 230, 0, 0.4)',
pointerEvents: 'none'
});
document.body.append(mark);
overlays.push(mark);
}
return () => overlays.forEach((el) => el.remove()); // Aufräumfunktion
};
const p = document.querySelector('p');
const range = document.createRange();
range.selectNodeContents(p);
const removeHighlight = highlightRange(range);
// später: removeHighlight();
Cursor-Position bzw. Zeilenanzahl bestimmen
// Position des Text-Cursors (zusammengeklappter Range)
const textNode = document.querySelector('#text').firstChild;
const caret = document.createRange();
caret.setStart(textNode, 10);
caret.collapse(true);
const [caretRect] = caret.getClientRects();
console.log(caretRect ? `Cursor bei x=${Math.round(caretRect.left)}` : 'Keine Position ermittelbar');
// Anzahl der Zeilen eines Absatzes schätzen (eindeutige top-Werte)
const lineRange = document.createRange();
lineRange.selectNodeContents(document.querySelector('#text'));
const lines = new Set(
Array.from(lineRange.getClientRects(), (r) => Math.round(r.top))
);
console.log(`Zeilen: ${lines.size}`);
// Wichtig · Fallstricke
- Viewport-Koordinaten: Die Werte ändern sich beim Scrollen. Für absolute Positionierung
window.scrollX/window.scrollYaddieren oder die Rechtecke relativ zum Container umrechnen. - Nicht live: Die zurückgegebene
DOMRectListist eine Momentaufnahme. Nach Resize, Schriftwechsel oder DOM-Änderungen neu abfragen. - Doppelte/überlappende Rechtecke: Enthält der Range ganze Elemente, liefern Browser sowohl das Element-Rechteck als auch die Rechtecke der enthaltenen Textzeilen. Zum Zeilenzählen daher nach
topdeduplizieren. - Zusammengeklappte Ranges: Bei einem kollabierten Range in einem Textknoten liefern moderne Browser in der Regel ein Rechteck mit Breite 0; an manchen Positionen (z. B. in leeren Elementen oder zwischen Elementknoten) kann die Liste leer sein – immer auf
lengthprüfen. - Performance: Der Aufruf erzwingt ein synchrones Layout (Reflow). In Schleifen oder Scroll-Handlern Lesen und Schreiben von Layout trennen bzw. mit
requestAnimationFramebündeln. - Nicht sichtbare Inhalte (
display: none) erzeugen keine Rechtecke. Transformationen (transform) werden berücksichtigt; die Rechtecke sind dann die achsenparallelen Hüllen. - Kompatibilität: In allen modernen Browsern verfügbar. Für reine Hervorhebungen ist die neuere CSS Custom Highlight API oft die bessere Wahl, da sie ohne Overlay-Elemente auskommt.