This specification defines additions to events for text and related input to allow for the monitoring and manipulation of default browser behavior in the context of text editor applications and other applications that deal with text input and text formatting. This specification builds on the UI events spec [[!UI-EVENTS]]
The group expects to produce another working draft by October 31st, 2016. The test suite and implementation reports for the Input Events specification are still work in progress.
This document describes editing related additions to 2 events - Input and beforeinput which are described in the UI events spec [[!UI-EVENTS]]. The goal of these events is to allow authors to understand and/or override default edit behavior both before and after editing occurs.
DataTransfer
This section is not normative.
A kill buffer is a in-memory store of richtext content that is separate from the clipboard which allows for the temporal storage of content that was deleted using specific deletion commands. The user can replace the current selection with the content held in the kill buffer by indicating an intention to yank the kill buffer.This section is not normative.
Creating a webbased texteditor requires a considerable amount of JavaScript on top of the browser code, among other things because:
This spec seeks to alleviate the problem by providing a simple way for web developers to both override browser handling of all user input related to text editing through the beforeinput event, and to monitor what browsers have changed in the DOM due to user input trough the input event.
This section is not normative.
Input events are sent before (beforeinput event) and after (input event) a user attempts to edit the markup. This includes characters, deletions, and other related edits.
The main part of the InputEvent is defined in [[!UI-EVENTS]]
partial interface InputEvent { readonly attribute DOMString inputType; readonly attribute DataTransfer? dataTransfer; sequence<StaticRange> getTargetRanges(); }; partial dictionary InputEventInit { DOMString inputType = ""; DataTransfer? dataTransfer = null; sequence<StaticRange> targetRanges = []; };
This section is not normative.
The following table provides a summary of when the data and dataTransfer attributes contain contents and when they are null as well as when the getTargetRanges() method returns an empty or non-empty Array, based on the inputType.
Editing host | inputType | data | dataTransfer | getTargetRanges() |
---|---|---|---|---|
Contenteditable |
"insertText" , "insertCompositionText" , "insertFromComposition" ,
"formatSetBlockTextDirection" , "formatSetInlineTextDirection" ,
"formatBackColor" , "formatFontColor" , "formatFontName" ,
"insertLink"
| Yes | null | Non-empty Array |
Contenteditable |
"insertFromPaste" ,
"insertFromDrop" , "insertReplacementText" ,
"insertFromYank"
| null | Yes | Non-empty Array |
<textarea> , <input type="text"> |
"insertText" , "insertCompositionText" , "insertFromComposition" ,
"insertFromPaste" ,
"insertFromDrop" , "insertReplacementText" ,
"insertFromYank" ,
"formatSetBlockTextDirection" , "formatSetInlineTextDirection" ,
"formatBackColor" , "formatFontColor" , "formatFontName" ,
"insertLink"
| Yes | null | Empty Array |
All |
"historyUndo" , "historyRedo"
| null | null | Empty Array |
Contenteditable | All Remaining | null | null | Non-empty Array |
<textarea> , <input type="text"> |
All Remaining | null | null | Empty Array |
inputType holds the type of input. Allowed
values are the following: "insertText"
, "insertReplacementText"
,
"insertLineBreak"
, "insertParagraph"
,
"insertOrderedList"
, "insertUnorderedList"
,
"insertHorizontalRule"
, "insertFromYank"
,
"insertFromDrop"
, "insertFromPaste"
,
"insertCompositionText"
, "insertFromComposition"
,
"deleteWordForward"
, "deleteWordBackward"
,
"deleteSoftLineForward"
, "deleteSoftLineBackward"
,
"deleteEntireSoftLine"
,
"deleteHardLineForward"
, "deleteHardLineBackward"
,
"deleteCompositionText"
, "deleteByComposition"
,
"deleteByDrag"
, "deleteByCut"
,
"deleteContent"
,
"deleteContentBackward"
, "deleteContentForward"
,
"historyUndo"
, "historyRedo"
,
"insertTranspose"
,
"formatBold"
, "formatItalic"
, "formatUnderline"
,
"formatStrikeThrough"
, "formatSuperscript"
,
"formatSubscript"
, "formatJustifyCenter"
, "formatJustifyRight"
,
"formatJustifyLeft"
, "formatIndent"
, "formatOutdent"
,
"formatRemove"
,
"formatSetBlockTextDirection"
, "formatSetInlineTextDirection"
,
"formatBackColor"
, "formatFontColor"
, "formatFontName"
,
"insertLink"
.
During initialization, the
user agent [[!UI-EVENTS]]
is responsible for populating this field as follows:
inputTypes
does not mean that any given implementation will support all of these.
But if a given browser supports an editing operation which potentially leads to
a change of the DOM, it MUST dispatch
[[!UI-EVENTS]] the corresponding beforeinput
and input
events.
"insertText"
.
"insertReplacementText"
.
"insertLineBreak"
.
"insertParagraph"
.
"insertOrderedList"
.
"insertUnorderedList"
.
"insertHorizontalRule"
.
"insertFromYank"
.
"insertFromDrop"
.
"insertFromPaste"
.
"insertTranspose"
.
"insertCompositionText"
.
false
."insertFromComposition"
.
"deleteCompositionText"
.
false
."deleteByComposition"
.
"deleteWordBackward"
.
"deleteWordForward"
.
"deleteSoftLineBackward"
.
"deleteSoftLineForward"
.
"deleteEntireSoftLine"
.
"deleteHardlineBackward"
.
"deleteHardlineForward"
.
"deleteByDrag"
.
"deleteByCut"
.
"deleteContent"
.
"deleteContentBackward"
.
"deleteContentBackward"
will be used both when
the user asks for text deletion within a text node,
and when the user shows the intention to deletion of more
complex elements or merge paragraphs if the caret is at the
start of a text node. In some scripts, backward
deletion within a text node with a collapsed selection
will delete a code point rather than a grapheme. A grapheme
can consist of several characters, where each character
corresponds to a code point. The getTargetRanges()
method can be used to find out how many characters the
browser will remove by default if deleting within a text
node."deleteContentForward"
.
"deleteContentForward"
will be used both when
the user asks for text deletion within a text node,
and when the user shows the intention to deletion of more
complex elements or merge paragraphs if the caret is at the
end of a text node. In some scripts, forward
deletion within a text node with a collapsed selection
will delete a grapheme rather than a code point. A grapheme
can consist of several characters, where each character
corresponds to a code point. The getTargetRanges()
method can be used to find out how many characters the
browser will remove by default if deleting within a text
node."historyUndo"
.
"historyRedo"
.
"formatBold"
.
"formatItalic"
.
"formatUnderline"
.
"formatStrikeThrough"
.
"formatSuperscript"
.
"formatSubscript"
.
"formatJustifyCenter"
.
"formatJustifyRight"
.
"formatJustifyLeft"
.
"formatIndent"
.
"formatOutdent"
.
"formatRemove"
.
"formatSetBlockTextDirection"
.
"formatSetInlineTextDirection"
.
"formatBackColor"
.
"formatFontColor"
.
"insertLink"
.
"formatFontName"
.
This section is not normative.
An IME composition causes several beforeinput events, not all of which can be canceled.
If the composition is starting with an empty initial composition string (i.e. the composition is not recomposing an existing range of the DOM), this step is skipped.
The UA extracts the initial composition string from the DOM.
A beforeinput event with the inputType
"deleteByComposition"
is triggered. The targetRanges of this beforeinput event is an
array of static ranges covering the part of the DOM from which the initial composition string was extracted.
The default action for this beforeinput event is the removal of the targetRanges from the DOM (i.e. the initial composition string will be removed from the DOM).
The beforeinput event can be canceled. Canceling the event will prevent the browser from removing the initial composition string from the DOM. Canceling or handling the event will not prevent the composition from taking place or influence the contents of the initial composition string.
The composition starts.
If the composition has an empty initial composition string, this step is skipped.
A non-cancelable beforeinput event with the inputType "insertCompositionText"
is triggered with the data attribute set to the initial composition string. The default action
for this event is the insertion of the initial composition string into the range of the DOM
controlled by the composition process.
With each DOM update of the composition string, a non-cancelable beforeinput event with the
inputType "insertCompositionText"
is triggered with the data attribute set to the
updated contents of the composition text. The default action for this event is the modification of range
of the DOM controlled by the composition as well as the composition string so that both match the value of
the data attribute of the beforeinput event.
This step is repeated as long as no text is to be committed to the DOM (i.e. is made to be part of the DOM outside the range controlled by the composition).
When the part or the entire composition string is to be committed to the DOM, a non-cancelable beforeinput
event with the inputType "deleteCompositionText"
is triggered. The default action
for this event is that the range controlled by the composition process is remvoed from the DOM is removed
from the DOM.
A cancelable beforeinput event with the inputType "insertFromComposition"
is triggered with the data attribute set to the part of the final composition string that is to be committed
to the DOM. The default action for this event is inserting the value of the data attribute into the DOM at the
place of the selection.
If only a part of the composition string was committed to the DOM, while another part is not yet to be committed, the process starts over from step 3 with the non-committed part of the final composition string as the new initial composition string.
The composition ends.
data
holds information plaintext data
related to what is to be added to the document.
"insertText"
, "insertCompositionText"
or "insertFromComposition"
, the value of data is to be the plain text
string to be inserted."insertFromPaste"
, "insertFromDrop"
, "insertTranspose"
,
"insertReplacementText"
, or "insertFromYank"
the value of data is to be
the plain text string to be inserted."formatSetBlockTextDirection"
the value of data is to be one of "ltr"
, "rtl"
, "auto"
or "null"
."formatSetInlineTextDirection"
or "formatFontColor"
the value of data is to be one of "ltr"
, "rtl"
, "auto"
or "null"
."formatBackColor"
or "formatFontColor"
the value of data is to be the hex digit value of the proposed color."formatFontName"
the value of data is to be the proposed value of the font-family CSS property."insertLink"
the value of data is to be the url of the proposed link.dataTransfer holds information about richtext and plaintext data that is to be taken from or added to the document in a DataTransfer object if there is relevant data.
"insertFromPaste"
,
"insertFromDrop"
, "insertTranspose"
,
"insertReplacementText"
, or "insertFromYank"
the dataTransfer attribute is a
prepopulated DataTransfer object so that:
"text/html"
, whose kind
is Plain Unicode string, and whose data is a HTML representation
of the content that is in the clipboard, in the kill buffer,
to be dropped or otherwise the content that is to be added. [[!HTML-LIVING]]"text/plain"
, whose kind
is Plain Unicode string, and whose data is a plain text
representation of the content that is to be pasted,
dropped or otherwise added. [[!HTML-LIVING]]getTargetRanges()
returns an Array of
StaticRanges
associated with this event, unless the inputType is
"historyUndo"
or "historyRedo"
or the editing host is not a contenteditable element,
in which case it returns an empty Array.
dictionary StaticRange { Node startContainer; unsigned long startOffset; Node endContainer; unsigned long endOffset; };
beforeinput
Type | beforeinput |
---|---|
Interface |
InputEvent
|
Sync / Async | Sync |
Bubbles | Yes |
Trusted Targets | Any Element with
contenteditable attribute
enabled. |
Default action [[!UI-EVENTS]] | Varies: 'Update the DOM' for contentEditable=typing
editing hosts for inputTypes "insertCompositionText"
and "deleteCompositionText" .
'Update the DOM element' for contentEditable="true"
editing hosts for all inputTypes. None otherwise. |
Context (trusted events) |
|
A user agent MUST dispatch [[!UI-EVENTS]] this event when the user has attempted to input in a contenteditable element. It does not necessarily mean the user agent [[!UI-EVENTS]] will then update the DOM.
A user agent MUST NOT dispatch [[!UI-EVENTS]] this event due to events that are not caused by attempted user input, such as system events.
input
Type | input |
---|---|
Interface |
InputEvent
|
Sync / Async | Sync |
Bubbles | Yes |
Trusted Targets | Any Element with
contenteditable attribute
enabled. |
Default action [[!UI-EVENTS]] | None |
Context (trusted events) |
|
A user agent MUST dispatch [[!UI-EVENTS]] this event immediately after the DOM has been updated due to a user expressed intention to change the document contents which the browser has handled.
Thanks to: Michael Aufreiter, Adrian Bateman, Oliver Buchtala, Robin Berjon, Enrica Casucci, Bo Cupp, Domenic Denicola, Emil Eklund, Olivier Forget, Aryeh Gregor, Marijn Haverbeke, Yoshifumi Inoue, Koji Ishii, Gary Kacmarcik, Ian Kilpatrick, Frederico Caldeira Knabben, Takayoshi Kochi, Piotrek Koszuliński, Travis Leithead, Grisha Lyukshin, Miles Maxfield, Chaals McCathie Nevile, Masayuki Nakano, Ryosuke Niwa, Julie Parent, Ben Peters, Florian Rivoal, Morgan Smith, Hallvord R. M. Steen, Johan Sörlin, Cristian Talau, Dave Tapuska, Ojan Vafai, Léonie Watson, Xiaoqian Wu, Chong Zhang, Joanmarie, and everyone in the Editing Taskforce for their input and feedback.