Signatur
Beschreibung
Die schreibgeschützte Eigenschaft offsetNode eines CaretPosition-Objekts gibt den Node zurück, in dem die Einfügemarke (Caret) an einer bestimmten Bildschirmkoordinate liegen würde. Ein CaretPosition-Objekt bekommst du über document.caretPositionFromPoint(x, y).
Zusammen mit CaretPosition.offset beschreibt offsetNode eine exakte Position im Dokument: Ist offsetNode ein Text-Knoten, steht offset für den Zeichenindex innerhalb des Textes, bei einem Element dagegen für den Index des Kindknotens. So lässt sich zum Beispiel ermitteln, auf welches Wort geklickt wurde, wo ein per Drag & Drop gezogenes Element eingefügt werden soll oder wo eine eigene Textauswahl beginnen soll.
Typische Einsatzfälle sind eigene Editoren, „Wort unter dem Mauszeiger nachschlagen“, Annotationswerkzeuge und das Positionieren eines Range-Objekts mit range.setStart(pos.offsetNode, pos.offset).
Rückgabewert
Text-Knoten, teils aber auch ein Element (z. B. ein <input> oder ein Container zwischen Kindknoten).Beispiele
Geklicktes Wort ermitteln
document.addEventListener('click', (event) => {
const pos = document.caretPositionFromPoint(event.clientX, event.clientY);
if (!pos) return;
const node = pos.offsetNode;
if (node.nodeType !== Node.TEXT_NODE) return;
const text = node.textContent;
let start = pos.offset;
let end = pos.offset;
// Wortgrenzen links und rechts suchen
while (start > 0 && /\S/.test(text[start - 1])) start--;
while (end < text.length && /\S/.test(text[end])) end++;
console.log('Geklicktes Wort:', text.slice(start, end));
});
Browserübergreifend mit Fallback auf caretRangeFromPoint
const getCaretAt = (x, y) => {
if (document.caretPositionFromPoint) {
const pos = document.caretPositionFromPoint(x, y);
return pos ? { node: pos.offsetNode, offset: pos.offset } : null;
}
// Ältere WebKit-/Blink-Versionen
if (document.caretRangeFromPoint) {
const range = document.caretRangeFromPoint(x, y);
return range ? { node: range.startContainer, offset: range.startOffset } : null;
}
return null;
};
const caret = getCaretAt(100, 50);
console.log(caret?.node.nodeName, caret?.offset);
Element an der Drop-Position in den Text einfügen
const editor = document.querySelector('#editor');
editor.addEventListener('dragover', (e) => e.preventDefault());
editor.addEventListener('drop', (e) => {
e.preventDefault();
const pos = document.caretPositionFromPoint(e.clientX, e.clientY);
if (!pos || !editor.contains(pos.offsetNode)) return;
const range = document.createRange();
range.setStart(pos.offsetNode, pos.offset);
range.collapse(true);
const marker = document.createElement('mark');
marker.textContent = e.dataTransfer.getData('text/plain');
range.insertNode(marker);
});
// Wichtig · Fallstricke
- Kompatibilität:
caretPositionFromPoint()und damitoffsetNodegibt es in Firefox schon lange, in Chromium erst ab Version 128 und in Safari ab 18.2. Für ältere Browser brauchst du den Fallback auf das nicht standardisiertedocument.caretRangeFromPoint(). caretPositionFromPoint()kannnullliefern, z. B. wenn die Koordinaten außerhalb des Viewports liegen. Prüfe das immer, bevor du aufoffsetNodezugreifst.- Der Knoten ist nicht immer ein
Text-Knoten: Bei<input>/<textarea>wird meist das Formularelement selbst geliefert, undoffsetbezieht sich dann auf dessenvalue. Prüfe deshalbnodeType. - Knoten in Shadow DOM werden standardmäßig nicht offengelegt. Moderne Browser nehmen dafür ein Optionsobjekt
{ shadowRoots: [...] }als dritten Parameter an. - Die Koordinaten sind Viewport-Koordinaten (
clientX/clientY), keine Seitenkoordinaten (pageX/pageY).