Signatur
Beschreibung
document.caretRangeFromPoint() ermittelt, an welcher Stelle im Textfluss eine Einfügemarke (Caret) läge, wenn man an den übergebenen Koordinaten klicken würde. Das Ergebnis ist ein zusammengeklapptes Range-Objekt: startContainer und endContainer sind identisch (meist ein Textknoten), ebenso startOffset und endOffset, also die Zeichenposition innerhalb dieses Knotens.
Typische Anwendungsfälle sind Editoren, die beim Ablegen per Drag & Drop die Einfügeposition bestimmen müssen, Wörterbuch- oder Übersetzungs-Tooltips, die das Wort unter dem Mauszeiger ermitteln, sowie eigene Markierungs- und Annotationswerkzeuge. Die Koordinaten beziehen sich auf den Viewport (wie clientX/clientY eines MouseEvent), nicht auf das gesamte Dokument.
Die Methode ist eine WebKit-/Blink-Erweiterung und nicht Teil eines Standards. Das standardisierte Gegenstück ist document.caretPositionFromPoint(), das ein CaretPosition-Objekt mit offsetNode und offset zurückgibt. Für browserübergreifenden Code sollte man zuerst auf die Standardmethode prüfen und caretRangeFromPoint() nur als Rückfall verwenden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $x Pflicht | number | Horizontale Koordinate in CSS-Pixeln relativ zum Viewport (z. B. event.clientX). |
|
| $y Pflicht | number | Vertikale Koordinate in CSS-Pixeln relativ zum Viewport (z. B. event.clientY). |
Rückgabewert
Range-Objekt an der Caret-Position oder null, wenn die Koordinaten außerhalb des Viewports liegen oder dort kein passender Knoten gefunden wird.Beispiele
Zeichenposition beim Klick ermitteln
document.addEventListener('click', (event) => {
const range = document.caretRangeFromPoint(event.clientX, event.clientY);
if (!range) return;
const node = range.startContainer;
console.log('Knoten:', node.nodeName, '| Offset:', range.startOffset);
});
Browserübergreifende Hilfsfunktion mit Fallback
const getCaretAt = (x, y) => {
// Standardmethode bevorzugen (Firefox, neuere Chrome/Safari)
if (document.caretPositionFromPoint) {
const pos = document.caretPositionFromPoint(x, y);
return pos ? { node: pos.offsetNode, offset: pos.offset } : null;
}
// WebKit/Blink-Fallback
if (document.caretRangeFromPoint) {
const range = document.caretRangeFromPoint(x, y);
return range ? { node: range.startContainer, offset: range.startOffset } : null;
}
return null;
};
document.addEventListener('mouseup', (e) => {
console.log(getCaretAt(e.clientX, e.clientY));
});
Wort unter dem Mauszeiger hervorheben
const article = document.querySelector('article');
article.addEventListener('dblclick', (event) => {
const range = document.caretRangeFromPoint(event.clientX, event.clientY);
if (!range || range.startContainer.nodeType !== Node.TEXT_NODE) return;
const text = range.startContainer.textContent;
let start = range.startOffset;
let end = range.startOffset;
// Wortgrenzen suchen
while (start > 0 && /\w/.test(text[start - 1])) start--;
while (end < text.length && /\w/.test(text[end])) end++;
range.setStart(range.startContainer, start);
range.setEnd(range.startContainer, end);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
console.log('Wort:', range.toString());
});
Einfügeposition beim Drop in einen Editor
const editor = document.querySelector('[contenteditable]');
editor.addEventListener('dragover', (e) => e.preventDefault());
editor.addEventListener('drop', (e) => {
e.preventDefault();
const range = document.caretRangeFromPoint(e.clientX, e.clientY);
if (!range) return;
const text = e.dataTransfer.getData('text/plain');
range.insertNode(document.createTextNode(text));
});
// Wichtig · Fallstricke
- Nicht standardisiert: Firefox unterstützt
caretRangeFromPoint()nicht. Verwende Feature-Erkennung und bevorzugedocument.caretPositionFromPoint()(Standard, inzwischen auch in Chrome 128+ und Safari verfügbar). - Viewport-Koordinaten: Übergib
clientX/clientY, nichtpageX/pageY– sonst liefert die Methode bei gescrollten Seiten falsche Ergebnisse odernull. - Liegen die Koordinaten außerhalb des sichtbaren Bereichs, ist der Rückgabewert
null– immer prüfen. - Der
startContainerist nicht zwingend ein Textknoten; bei Bildern, leeren Elementen oder Formularfeldern kann es ein Element sein. Innerhalb von<input>/<textarea>ist das Verhalten uneinheitlich. - Elemente mit
pointer-events: noneoderuser-select: nonekönnen das Ergebnis beeinflussen; Inhalte in Shadow-DOM werden je nach Browser nicht aufgelöst. - Bei häufigem Aufruf (z. B. in
mousemove) Layout-Neuberechnungen bedenken und ggf. perrequestAnimationFramedrosseln.