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

Range.getClientRects()

Methode

Gibt eine <code>DOMRectList</code> mit den Rechtecken zurück, die der Inhalt eines <code>Range</code> auf dem Bildschirm belegt, relativ zum Viewport.

seit JavaScript Baseline: alle modernen Browser (seit 2015 weitgeh Kategorie: webapi

Signatur

Range.prototype.getClientRects()

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

Typ
DOMRectList
Beschreibung
Eine live nicht aktualisierte, array-ähnliche Liste von 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)}`);
  });
});
Auswahl belegt 3 Rechteck(e) #0: left=120, top=84, width=410 #1: left=16, top=108, width=620 #2: left=16, top=132, width=95

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}`);
Cursor bei x=87 Zeilen: 4

// Wichtig · Fallstricke

  • Viewport-Koordinaten: Die Werte ändern sich beim Scrollen. Für absolute Positionierung window.scrollX/window.scrollY addieren oder die Rechtecke relativ zum Container umrechnen.
  • Nicht live: Die zurückgegebene DOMRectList ist 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 top deduplizieren.
  • 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 length prüfen.
  • Performance: Der Aufruf erzwingt ein synchrones Layout (Reflow). In Schleifen oder Scroll-Handlern Lesen und Schreiben von Layout trennen bzw. mit requestAnimationFrame bü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.