Signatur
Beschreibung
EditContext ist das zentrale Objekt der EditContext API. Damit lassen sich eigene Editoren bauen, zum Beispiel für Code, Rich Text oder auf einem <canvas> gezeichneten Text, ohne auf contenteditable angewiesen zu sein. Der Browser liefert die Texteingabe, auch über Eingabemethoden (IME) für Chinesisch, Japanisch oder Koreanisch, über Bildschirmtastaturen und über Diktierfunktionen. Das Rendern übernimmt vollständig die eigene Anwendung.
Verknüpft wird der Kontext über die Eigenschaft element.editContext. Sobald das Element den Fokus hat, feuert der Kontext das Ereignis textupdate, wenn Text eingefügt, gelöscht oder ersetzt wird. Die Anwendung aktualisiert daraufhin ihr eigenes Modell und zeichnet die Ansicht neu. Weitere Ereignisse sind textformatupdate (Unterstreichungen während einer IME-Komposition), characterboundsupdate (der Browser braucht Zeichenpositionen, um das IME-Fenster zu platzieren) sowie compositionstart und compositionend.
Wichtige Eigenschaften (nur lesbar): text, selectionStart, selectionEnd und characterBoundsRangeStart. Wichtige Methoden:
updateText(rangeStart, rangeEnd, text)ändert den Text des Kontexts, wenn er von außen geändert wird (z. B. Einfügen per Skript).updateSelection(start, end)hält die Auswahl mit dem Editor-Modell synchron.updateControlBounds(rect)undupdateSelectionBounds(rect)teilen dem Betriebssystem die Position des Editors bzw. der Auswahl mit (alsDOMRectin Viewport-Koordinaten).updateCharacterBounds(rangeStart, rects)liefert die Rechtecke der einzelnen Zeichen, meist als Antwort aufcharacterboundsupdate.attachedElements()gibt die verknüpften Elemente zurück,characterBounds()die zuletzt gemeldeten Zeichenrechtecke.
Die Schnittstelle lohnt sich vor allem für anspruchsvolle Editoren wie Web-IDEs oder Office-Anwendungen, die volle Kontrolle über Layout und Rendering brauchen und trotzdem sauber mit IME und Barrierefreiheit umgehen sollen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | Object | {} | Optionales Objekt für den Konstruktor new EditContext(options). |
| $options.text | string | "" | Anfangstext des Bearbeitungskontexts. |
| $options.selectionStart | number | 0 | Startposition der Auswahl (Offset in UTF-16-Codeeinheiten). |
| $options.selectionEnd | number | 0 | Endposition der Auswahl. Ist sie gleich selectionStart, ist nur der Cursor gesetzt und nichts markiert. |
Rückgabewert
EditContext-Instanz. Sie wird über element.editContext = ctx an ein Element gebunden.Beispiele
EditContext erzeugen und an ein Canvas binden
const canvas = document.querySelector('#editor');
canvas.tabIndex = 0; // Canvas fokussierbar machen
const ctx2d = canvas.getContext('2d');
const editContext = new EditContext({ text: 'Hallo', selectionStart: 5, selectionEnd: 5 });
canvas.editContext = editContext;
const render = () => {
ctx2d.clearRect(0, 0, canvas.width, canvas.height);
ctx2d.font = '20px monospace';
ctx2d.fillText(editContext.text, 10, 30);
};
editContext.addEventListener('textupdate', (event) => {
console.log(`Ersetze ${event.updateRangeStart}–${event.updateRangeEnd} durch "${event.text}"`);
render();
});
render();
console.log(editContext.attachedElements()[0] === canvas);
Editor mit div: eigene Darstellung und Auswahl synchron halten
const view = document.querySelector('#code-view');
const editContext = new EditContext();
view.editContext = editContext;
const renderView = () => {
view.textContent = editContext.text;
};
editContext.addEventListener('textupdate', () => {
renderView();
});
// Text per Skript einfügen, z. B. über eine Toolbar-Schaltfläche
document.querySelector('#insert-snippet').addEventListener('click', () => {
const pos = editContext.selectionEnd;
const snippet = 'console.log();';
editContext.updateText(pos, pos, snippet);
editContext.updateSelection(pos + snippet.length, pos + snippet.length);
renderView();
});
IME-Unterstützung: Positionen und Kompositionsformate melden
const canvas = document.querySelector('#editor');
const editContext = new EditContext();
canvas.editContext = editContext;
const charWidth = 12, lineHeight = 24;
// Position des Editors dem Betriebssystem mitteilen
const syncBounds = () => {
const rect = canvas.getBoundingClientRect();
editContext.updateControlBounds(rect);
const x = rect.left + editContext.selectionStart * charWidth;
editContext.updateSelectionBounds(new DOMRect(x, rect.top, 1, lineHeight));
};
editContext.addEventListener('characterboundsupdate', (event) => {
const rect = canvas.getBoundingClientRect();
const bounds = [];
for (let i = event.rangeStart; i < event.rangeEnd; i++) {
bounds.push(new DOMRect(rect.left + i * charWidth, rect.top, charWidth, lineHeight));
}
editContext.updateCharacterBounds(event.rangeStart, bounds);
});
editContext.addEventListener('textformatupdate', (event) => {
for (const format of event.getTextFormats()) {
console.log(format.rangeStart, format.rangeEnd, format.underlineStyle, format.underlineThickness);
}
});
editContext.addEventListener('compositionstart', () => console.log('Komposition beginnt'));
editContext.addEventListener('compositionend', () => console.log('Komposition beendet'));
canvas.addEventListener('focus', syncBounds);
window.addEventListener('resize', syncBounds);
Feature-Erkennung mit Rückfall auf contenteditable
const el = document.querySelector('#editor-host');
if ('EditContext' in window) {
el.editContext = new EditContext();
console.log('EditContext aktiv');
} else {
el.contentEditable = 'true';
console.log('Rückfall auf contenteditable');
}