Signatur
Beschreibung
Mit updateCharacterBounds() sagt ein Editor, der auf der EditContext-API aufbaut, dem Betriebssystem, wo genau einzelne Zeichen auf dem Bildschirm stehen. Diese Angaben braucht die Texteingabe des Systems (IME, Input Method Editor), zum Beispiel bei der Eingabe von Chinesisch, Japanisch oder Koreanisch. Nur mit ihnen erscheint das Fenster mit den Kandidaten direkt neben dem Text, der gerade zusammengesetzt wird.
Normalerweise rufst du die Methode im Handler des Ereignisses characterboundsupdate auf. Das Ereignis liefert in event.rangeStart und event.rangeEnd den Zeichenbereich, für den das System Maße anfordert. Du berechnest dann für jedes Zeichen in diesem Bereich ein DOMRect und übergibst die Liste zusammen mit dem Startindex.
Das ist vor allem dann wichtig, wenn du Text selbst zeichnest, etwa in einem <canvas>. Dort kennt der Browser die Lage der Zeichen nicht und kann sie dem System nicht selbst melden. Die Rechtecke müssen in Koordinaten relativ zum sichtbaren Bereich des Fensters (Viewport) angegeben werden, also im selben Bezugssystem wie getBoundingClientRect().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $rangeStart Pflicht | number | Position des ersten Zeichens, für das Maße übergeben werden, gezählt ab dem Anfang von editContext.text. Meist wird event.rangeStart übernommen. |
|
| $characterBounds Pflicht | Array<DOMRect> | Liste mit einem DOMRect pro Zeichen, beginnend bei rangeStart. Die Koordinaten beziehen sich auf den Viewport. |
Rückgabewert
Beispiele
Einfacher Aufruf mit festen Zeichenbreiten
const editor = document.querySelector('#editor');
const editContext = new EditContext({ text: 'Hallo' });
editor.editContext = editContext;
editContext.addEventListener('characterboundsupdate', (event) => {
const box = editor.getBoundingClientRect();
const charWidth = 10; // Monospace-Annahme
const lineHeight = 20;
const bounds = [];
for (let i = event.rangeStart; i < event.rangeEnd; i++) {
bounds.push(new DOMRect(box.x + i * charWidth, box.y, charWidth, lineHeight));
}
editContext.updateCharacterBounds(event.rangeStart, bounds);
console.log(`Maße für ${bounds.length} Zeichen gemeldet`);
});
Canvas-Editor: Zeichenbreiten mit measureText berechnen
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.font = '16px sans-serif';
const editContext = new EditContext();
canvas.editContext = editContext;
const PADDING = 8;
const LINE_HEIGHT = 22;
editContext.addEventListener('characterboundsupdate', ({ rangeStart, rangeEnd }) => {
const rect = canvas.getBoundingClientRect();
const text = editContext.text;
const bounds = [];
for (let i = rangeStart; i < rangeEnd; i++) {
// x-Position = Breite des Textes vor dem Zeichen
const x = ctx.measureText(text.slice(0, i)).width;
const w = ctx.measureText(text[i] ?? '').width;
bounds.push(new DOMRect(rect.left + PADDING + x, rect.top + PADDING, w, LINE_HEIGHT));
}
editContext.updateCharacterBounds(rangeStart, bounds);
});
Mit updateControlBounds und updateSelectionBounds kombinieren
const el = document.querySelector('#editor');
const editContext = new EditContext();
el.editContext = editContext;
const syncBounds = () => {
const box = el.getBoundingClientRect();
editContext.updateControlBounds(box);
// Rechteck der Einfügemarke (hier vereinfacht)
editContext.updateSelectionBounds(new DOMRect(box.x, box.y, 1, 20));
};
el.addEventListener('focus', syncBounds);
window.addEventListener('scroll', syncBounds, { passive: true });
editContext.addEventListener('characterboundsupdate', (e) => {
const box = el.getBoundingClientRect();
const bounds = Array.from({ length: e.rangeEnd - e.rangeStart }, (_, k) =>
new DOMRect(box.x + (e.rangeStart + k) * 9, box.y, 9, 20)
);
editContext.updateCharacterBounds(e.rangeStart, bounds);
});
// Wichtig · Fallstricke
- Experimentell: Die EditContext-API gibt es bisher nur in Chromium-Browsern (Chrome/Edge ab 121). Prüfe die Unterstützung vorher mit
'EditContext' in windowund plane einen Ersatz ein, zum Beispielcontenteditableoder ein verstecktes<textarea>. - Die Rechtecke müssen relativ zum Viewport angegeben werden, nicht relativ zum Canvas oder zum Dokument. Wird gescrollt oder das Layout ändert sich, stimmen früher gemeldete Werte nicht mehr.
- Pro Zeichen im angefragten Bereich wird genau ein
DOMRecterwartet. Fehlen Einträge oder sind die Werte falsch, erscheint das IME-Fenster an der falschen Stelle. - Fehlen Argumente oder ist
rangeStartkeine Zahl bzw.characterBoundskeine Liste vonDOMRect-Objekten, wird einTypeErrorausgelöst. - Die Positionen beziehen sich auf die Einheiten von
editContext.text(UTF-16-Codeeinheiten). Bei Emoji oder Ersatzzeichenpaaren (Surrogatpaaren) musst du die Indizes entsprechend behandeln. - Leistung:
measureTextfür jede Teilzeichenkette ist bei langen Texten teuer. Speichere berechnete Positionen zwischen (Cache).