Signatur
Beschreibung
Das Ereignis characterboundsupdate gehört zur EditContext-API. Mit ihr bauen Sie eigene Texteditoren, etwa auf Basis eines <canvas> oder eines selbst gerenderten DOM, die trotzdem vollständig mit Eingabemethoden (IME) für Chinesisch, Japanisch, Koreanisch oder Emoji-Auswahl zusammenarbeiten. Die IME muss wissen, wo auf dem Bildschirm die Zeichen der aktuellen Komposition stehen. Nur dann kann sie ihr Kandidatenfenster direkt am Text anzeigen.
Weil der Browser Ihre selbst gezeichneten Zeichen nicht ausmessen kann, fragt er über dieses Ereignis nach. Das Ereignisobjekt ist ein CharacterBoundsUpdateEvent mit den Eigenschaften rangeStart und rangeEnd. Sie geben den Bereich der Zeichen-Offsets im Text des EditContext an, für den Grenzen gebraucht werden. Ihr Handler berechnet für jedes Zeichen in diesem Bereich ein DOMRect in Viewport-Koordinaten und übergibt die Liste mit editContext.updateCharacterBounds(rangeStart, rects).
Das Ereignis tritt meist während einer Komposition auf, also zwischen compositionstart und compositionend, und oft nach einem textupdate. Sie können den Handler auf zwei Arten registrieren: mit addEventListener("characterboundsupdate", …) oder über die Eigenschaft editContext.oncharactersboundsupdate. Richtig geschrieben heißt sie oncharacterboundsupdate.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "characterboundsupdate". |
|
| $handler Pflicht | (event: CharacterBoundsUpdateEvent) => void | Callback, der ein CharacterBoundsUpdateEvent erhält. Wichtige Eigenschaften: event.rangeStart (Offset des ersten Zeichens) und event.rangeEnd (Offset nach dem letzten Zeichen, exklusiv). |
|
| $options | boolean|AddEventListenerOptions | false | Übliche Listener-Optionen wie once oder signal (AbortSignal zum Entfernen). |
Rückgabewert
addEventListener gibt nichts zurück. Die Antwort an die IME geben Sie im Handler über editContext.updateCharacterBounds().Beispiele
Zeichengrenzen für einen Canvas-Editor melden
const canvas = document.querySelector('#editor');
const ctx2d = canvas.getContext('2d');
ctx2d.font = '20px monospace';
const editContext = new EditContext();
canvas.editContext = editContext;
const LINE_Y = 30; // Grundlinie der Textzeile im Canvas
const PADDING_X = 10;
const LINE_HEIGHT = 24;
editContext.addEventListener('characterboundsupdate', (event) => {
const { rangeStart, rangeEnd } = event;
const canvasRect = canvas.getBoundingClientRect();
const text = editContext.text;
const rects = [];
for (let i = rangeStart; i < rangeEnd; i++) {
// x-Position = Breite des Textes vor dem Zeichen
const x = PADDING_X + ctx2d.measureText(text.slice(0, i)).width;
const width = ctx2d.measureText(text[i] ?? ' ').width;
rects.push(new DOMRect(
canvasRect.left + x,
canvasRect.top + LINE_Y - LINE_HEIGHT + 4,
width,
LINE_HEIGHT
));
}
editContext.updateCharacterBounds(rangeStart, rects);
console.log(`Grenzen für ${rangeStart}–${rangeEnd} gemeldet`);
});
DOM-basierter Editor: Grenzen per Range ermitteln
const editorEl = document.querySelector('#editor'); // enthält einen einzigen Textknoten
const editContext = new EditContext({ text: editorEl.textContent });
editorEl.editContext = editContext;
editContext.addEventListener('textupdate', (e) => {
editorEl.textContent = editContext.text;
});
editContext.addEventListener('characterboundsupdate', ({ rangeStart, rangeEnd }) => {
const textNode = editorEl.firstChild;
const range = document.createRange();
const rects = [];
for (let i = rangeStart; i < rangeEnd; i++) {
range.setStart(textNode, i);
range.setEnd(textNode, i + 1);
rects.push(range.getBoundingClientRect());
}
editContext.updateCharacterBounds(rangeStart, rects);
});
Handler über die on-Property setzen und entfernen
const editContext = new EditContext();
document.querySelector('#editor').editContext = editContext;
const logBounds = (event) => {
console.log('IME fragt Zeichen', event.rangeStart, 'bis', event.rangeEnd);
editContext.updateCharacterBounds(event.rangeStart, []);
};
// Variante 1: on-Property
editContext.oncharacterboundsupdate = logBounds;
// Später entfernen
editContext.oncharacterboundsupdate = null;
// Variante 2: addEventListener mit AbortController
const controller = new AbortController();
editContext.addEventListener('characterboundsupdate', logBounds, { signal: controller.signal });
controller.abort(); // Listener wird entfernt
// Wichtig · Fallstricke
- Browser-Kompatibilität: Die EditContext-API ist experimentell und gibt es derzeit nur in Chromium-Browsern (Chrome/Edge ab 121). Prüfen Sie vorab mit
if ('EditContext' in window)und bieten Sie sonst einen Ausweichweg an, etwacontenteditableoder ein verstecktes<textarea>. - Koordinaten: Die
DOMRect-Werte müssen im Viewport-Koordinatensystem stehen, genau wie beigetBoundingClientRect(). Canvas-interne Koordinaten müssen Sie also mit der Position des Canvas verrechnen. Wenn das Canvas per CSS skaliert wird, rechnen Sie zusätzlich die Skalierung (devicePixelRatiobzw. das Verhältnis von CSS-Größe zu Canvas-Größe) ein. - Offsets:
rangeStartundrangeEndsind UTF-16-Code-Unit-Offsets ineditContext.text. Bei Emojis und Surrogatpaaren passen sie nicht zu sichtbaren Zeichen (Graphemen).rangeEndist exklusiv. - Rufen Sie
updateCharacterBounds()synchron im Handler auf. Sonst zeigt die IME ihr Kandidatenfenster an der falschen Stelle an. Melden Sie außerdem Änderungen am Layout, etwa durch Scrollen oder Größenänderung, zusätzlich mitupdateControlBounds()undupdateSelectionBounds(). - Das Ereignis sagt nichts darüber, ob sich der Text geändert hat. Textänderungen verarbeiten Sie im Ereignis
textupdate.