Signatur
Beschreibung
Die Eigenschaft offset eines CaretPosition-Objekts gibt als Ganzzahl an, an welcher Stelle innerhalb des Knotens offsetNode die Einfügemarke liegt. CaretPosition-Objekte bekommt man über document.caretPositionFromPoint(x, y), das zu Bildschirmkoordinaten den nächstgelegenen Punkt im Dokument ermittelt.
Was der Wert bedeutet, hängt vom Knotentyp ab:
- Ist
offsetNodeein Textknoten (oder ein andererCharacterData-Knoten), istoffsetdie Anzahl der UTF-16-Codeeinheiten vor dem Cursor. - Bei einem Elementknoten ist
offsetder Index des Kindknotens, vor dem der Cursor liegt.
So kann man z. B. herausfinden, auf welches Wort ein Nutzer geklickt hat, an einer bestimmten Stelle einen Range anlegen, Text an der Mausposition einfügen oder eigene Drag-and-Drop-Editoren bauen. offset und offsetNode entsprechen genau den Argumenten, die Range.setStart() und Selection.collapse() erwarten.
Rückgabewert
Beispiele
Zeichenposition beim Klick bestimmen
document.addEventListener('click', (event) => {
const pos = document.caretPositionFromPoint?.(event.clientX, event.clientY);
if (!pos) return;
const { offsetNode, offset } = pos;
if (offsetNode.nodeType === Node.TEXT_NODE) {
const text = offsetNode.textContent;
console.log(`Offset ${offset}: …${text.slice(Math.max(0, offset - 5), offset)}|${text.slice(offset, offset + 5)}…`);
}
});
Angeklicktes Wort ermitteln
const wordAt = (text, offset) => {
const left = text.slice(0, offset).search(/\S+$/);
const right = text.slice(offset).search(/\s/);
const start = left === -1 ? offset : left;
const end = right === -1 ? text.length : offset + right;
return text.slice(start, end);
};
document.querySelector('article').addEventListener('dblclick', (e) => {
const pos = document.caretPositionFromPoint(e.clientX, e.clientY);
if (pos?.offsetNode.nodeType === Node.TEXT_NODE) {
console.log('Wort:', wordAt(pos.offsetNode.data, pos.offset));
}
});
Marker an der Mausposition einfügen (mit Fallback)
const getCaret = (x, y) => {
if (document.caretPositionFromPoint) {
const p = document.caretPositionFromPoint(x, y);
return p && { node: p.offsetNode, offset: p.offset };
}
// Fallback für ältere WebKit/Blink-Browser
const r = document.caretRangeFromPoint?.(x, y);
return r && { node: r.startContainer, offset: r.startOffset };
};
document.body.addEventListener('click', (e) => {
const caret = getCaret(e.clientX, e.clientY);
if (!caret) return;
const range = document.createRange();
range.setStart(caret.node, caret.offset);
range.collapse(true);
const mark = document.createElement('span');
mark.textContent = '📍';
range.insertNode(mark);
});
// Wichtig · Fallstricke
- Einheit: Bei Textknoten zählt
offsetUTF-16-Codeeinheiten, nicht sichtbare Zeichen. Emojis oder Zeichen außerhalb der BMP belegen zwei Einheiten, daher kanntext.slice(0, offset)ein Ersatzzeichenpaar (Surrogat) mitten durchtrennen. - Knotentyp prüfen: Bei Elementknoten ist
offsetein Kindindex und kein Zeichenindex. Deshalb immer zuerstoffsetNode.nodeTypeprüfen. - Formularfelder: Bei
<input>und<textarea>kannoffsetNodedas Feld selbst sein.offsetbezieht sich dann auf den Wert des Felds. Das Verhalten war in älteren Browsern nicht einheitlich. - Kompatibilität:
caretPositionFromPoint()gibt es in Firefox schon lange. Chrome/Edge unterstützen es erst ab Version 128, Safari erst in sehr neuen Versionen. Für ältere Browser das nicht standardisiertedocument.caretRangeFromPoint()als Ersatz verwenden (dannstartOffsetstattoffset). - Liegt der Punkt außerhalb des sichtbaren Bereichs, liefert
caretPositionFromPoint()null. Vor dem Zugriff aufoffsetalso prüfen. - Shadow DOM: Knoten in Shadow-Roots werden nur gefunden, wenn diese im Optionsobjekt
{ shadowRoots: [...] }übergeben werden.