This document is archived. See here for the current EditContext proposal.
The {{EditContext}} is a new API that allows authors to more directly participate in the text input process.
Modern operating systems provide mechanisms to produce text in a variety of ways: speech-to-text, virtual keyboards, handwriting recognition and many more. When an app wants to consume text input from these various sources, it must first provide a view of its currently editable text to the operating system. The view of editable text provides a common language that apps (having a variety of different document models) and sources of text (having a variety of different input methods) can both understand. Both the apps and input sources communicate with one another by expressing their desired changes to the state of the common view as an event that the other can handle to facilitate the text input process.
For the purposes of this document, a producer of text is known as a Text Input Method. The view provided by an app which wants to consume text is called a Text Edit Context. The service provided by the OS to facilitate the editing of text in the [=Text Edit Context=] by the [=Text Input Methods=] is called a Text Input Service.
Here’s a typical flow for the text input process in more detail:
Existing user agents handle the details of this text input process so that the author’s responsibility ends at declaring what elements of the document represent an editable region. Authors express which regions are editable using input elements, textarea elements, contenteditable elements, or by setting the designMode attribute to true to mark an entire document as editable.
As an editable region of the document is focused, the user agent automatically produces the [=Text Edit Context=] from the contents of the editable region and the position of the selection within it. When a [=Text Input Method=] produces text, the user agent translates the events against its [=Text Edit Context=] into a set of DOM and style modifications – only some of which are described using existing events that an author can handle.
Authors that want to produce sophisticated editing experiences may be challenged by the current approach. If, for example, the text and selection are rendered to a canvas, user agents are unable to produce a [=Text Edit Context=] to drive the text input process. Authors compensate by resorting to offscreen editable elements, but this approach comes with negative implications for accessibility, it deteriorates the input experience, and requires complex code to synchronize the position of the text in the offscreen editable element with the corresponding text in the canvas.
With the introduction of this EditContext API, authors can more directly participate in the protocol for text input and avoid the pitfalls described above.
An {{EditContext}} is a JavaScript projection of the [=Text Edit Context=] concept discussed in the previous section. Using an {{EditContext}}, an author can mark a region of the document editable by associating an instance of an {{EditContext}} with an element.
Associating an {{EditContext}} to an element makes that element intrinsically focusable. When the element is focused, the user agent will use the state of the {{EditContext}} to construct a [=Text Edit Context=] that is provided to the [=Text Input Service=] of the OS:
Using an EditContext, an author can mark a region of the document editable by associating an EditContext object with an element as shown in the example below:
In the example below, the author is using a canvas to draw an editable region that allows the user to input a single line of text rendered with a monospace font. The text for the editable region is maintained by the author as a String. The text offsets for the selection in the editable region are maintained by the author as a pair of Numbers: selectionStart and selectionEnd. The Numbers refer to the count of the number of UTF-16 codepoints to the left of the start and end of the selection respectively. For the sake of communicating the bounding boxes for the current selection and the editable region of the document to Text Input Services, the author also computes the bounding rectangle in CSS pixels for the selection and the editable region of the document. The offset of the rectangle is expressed relative to the origin of the canvas element since that is the element to which the author has associated an EditContext. Since the model for the author’s representation of text and selection location matches the form expected by the EditContext API, the author can simply assign those properties to the EditContext associated with the canvas whenever those values change.
Building on the previous example, in response to user input, authors should handle the events of both the editable element (in this case a canvas) and the EditContext.
Input events against the DOM continue to describe the user’s intent
Against the EditContext, {{TextUpdateEvent}} describes changes to the text, the selection, and the composition range properties of the EditContext. {{TextFormatUpdateEvent}} describes changes to the style of the text. The updates received by the author’s code for text, selection, style, composition range changes should be rendered back to the canvas so the user can see what they are typing. {{CharacterBoundsUpdateEvent}} describes what character bounds are needed by the [=Text Input Service=] to support [=Text Input Method=] to properly display its user interface. After receiving {{CharacterBoundsUpdateEvent}}, the author is responsible to compute the requested character bounds and call {{EditContext/updateCharacterBounds}} to update the character bounds cached in the EditContext.
Below example shows how to handle {{TextUpdateEvent}}, {{TextFormatUpdateEvent}}, and {{CharacterBoundsUpdateEvent}} to update the model and render the result to the canvas.
An author doesn’t have to use a canvas element with an EditContext. In the example below the author uses a div to establish an editable region of the document and renders the contents into that editable region using various other styled elements, images and text. This allows the author to leverage other built-in editing primitives from the user agent such as selection and spellcheck.
This specification defines conformance criteria that apply to a single product: the user agent that implements the interfaces that it contains.
Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)
partial interface Element { attribute EditContext? editContext; };editContext
An {{EditContext}}, initially null.
When an element is associated with an {{EditContext}}, it is focusable regardless of whether its contentediable attribute is true or not.
An {{EditContext}} is activated when the associated element is focused and deactivated when the associated element is blurred.
When an {{EditContext}} is activated, user text input MUST NOT modify DOM, instead, below steps should be implemented:
If in a composition:
If not in a composition:
An {{EditContext}} can be associated with multiple elements but only the element that has focus will receive beforeinput events.
Below is the list of elements that can be associated with {{EditContext}}: abbr, address, article, aside, b, blockquote, canvas, cite, code, del, details, dfn, div, dl, dd, dt, em, footer, form, h1, h2, h3, h4, h5, h6, hgroup, i, ins, kbd, label, mark, object, ol, output, p, pre, q, ruby, s, samp, section, small, span, strong, sub, sup, table, time, u, and ul.
dictionary EditContextInit { DOMString text; unsigned long selectionStart; unsigned long selectionEnd; }; [Exposed=Window] interface EditContext : EventTarget { constructor(optional EditContextInit options = {}); undefined updateText(unsigned long rangeStart, unsigned long rangeEnd, DOMString text); undefined updateSelection(unsigned long start, unsigned long end); undefined updateControlBound(DOMRect controlBound); undefined updateSelectionBound(DOMRect selectionBound); undefined updateCharacterBounds(unsigned long rangeStart, sequence characterBounds); sequence attachedElements(); readonly attribute DOMString text; readonly attribute unsigned long selectionStart; readonly attribute unsigned long selectionEnd; readonly attribute unsigned long compositionRangeStart; readonly attribute unsigned long compositionRangeEnd; readonly attribute boolean isInComposition; readonly attribute DOMRect controlBound; readonly attribute DOMRect selectionBound; readonly attribute unsigned long characterBoundsRangeStart; sequence characterBounds(); attribute EventHandler ontextupdate; attribute EventHandler ontextformatupdate; attribute EventHandler oncharacterboundsupdate; attribute EventHandler oncompositionstart; attribute EventHandler oncompositionend; };
The control bound may be used by the OS for hittesting to trigger the virtual keyboard.
The method must follow these steps:
The method must follow these steps:
The method must follow these steps:
The method must follow these steps:
The method must follow these steps:
The method must follow these steps:
The method returns elements that are associated with the EditContext.
should attachedElements() return elements that are removed from the tree? If yes, the EditContext will keep removed elements alive. If not, we probably shouldn't allow a disconnected element to associate with EditContext, otherwise, it would be confusing/unintuitive that we allow it but attachedElements returns empty.
The event handler for {{TextUpdateEvent}}.
The event handler for {{CharacterBoundsUpdateEvent}}.
The event handler for {{TextFormatUpdateEvent}}.
The event handler for the compositionstart event.
The event handler for the compositionend event.
dictionary TextUpdateEventInit { unsigned long updateRangeStart; unsigned long updateRangeEnd; DOMString text; unsigned long selectionStart; unsigned long selectionEnd; unsigned long compositionStart; unsigned long compositionEnd; }; [Exposed=Window] interface TextUpdateEvent : Event { constructor(optional TextUpdateEventInit options = {}); readonly attribute unsigned long updateRangeStart; readonly attribute unsigned long updateRangeEnd; readonly attribute DOMString text; readonly attribute unsigned long selectionStart; readonly attribute unsigned long selectionEnd; readonly attribute unsigned long compositionStart; readonly attribute unsigned long compositionEnd; };
dictionary TextFormatInit { unsigned long rangeStart; unsigned long rangeEnd; DOMString textColor; DOMString backgroundColor; DOMString underlineStyle; DOMString underlineThickness; DOMString underlineColor; }; [Exposed=Window] interface TextFormat { constructor(optional TextFormatInit options = {}); attribute unsigned long rangeStart; attribute unsigned long rangeEnd; attribute DOMString textColor; attribute DOMString backgroundColor; attribute DOMString underlineStyle; attribute DOMString underlineThickness; attribute DOMString underlineColor; }; dictionary TextFormatUpdateEventInit { sequence textFormats; }; [Exposed=Window] interface TextFormatUpdateEvent : Event { constructor(optional TextFormatUpdateEventInit options = {}); sequence getTextFormats(); };
dictionary CharacterBoundsUpdateEventInit { unsigned long rangeStart; unsigned long rangeEnd; }; [Exposed=Window] interface CharacterBoundsUpdateEvent : Event { constructor(optional CharacterBoundsUpdateEventInit options = {}); readonly attribute unsigned long rangeStart; readonly attribute unsigned long rangeEnd; };
To mitigate potential security and privacy risks, browsers are expected to follow best practices described below.
Add contributors