Skip to content

TextInput Events

Maciej Jastrzebski edited this page Aug 7, 2023 · 32 revisions

Single Line - basic flow

RN version: 0.71.6

Setup:

<TextInput
  style={styles.textInput}
  value={value}
  onChangeText={handleChangeText}
  onPressIn={handlePressIn}
  onPressOut={handlePressOut}
  onFocus={handleFocus}
  onBlur={handleBlur}
  onChange={handleChange}
  onEndEditing={handleEndEditing}
  onSubmitEditing={handleSubmitEditing}
  onKeyPress={handleKeyPress}
  onTextInput={handleTextInput}
  onSelectionChange={handleSelectionChange}
  onContentSizeChange={handleContentSizeChange}
/>

Steps:

  1. Press on the text input
  2. Type "Test" using on screen keyboard
  3. Press "return" to close the keyboard

Output:

iOS:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 1, "locationX": 153, "locationY": 10.333328247070312, "pageX": 173, "pageY": 121.33332824707031, "target": 75, "timestamp": 975406399.731625, "touches": [[Circular]]}
LOG  Event: focus {"eventCount": 0, "target": 75, "text": ""}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 153, "locationY": 10.333328247070312, "pageX": 173, "pageY": 121.33332824707031, "target": 75, "timestamp": 975406478.905625, "touches": []}

LOG  Event: keyPress {"eventCount": 0, "key": "T", "target": 75}
LOG  Event: textInput {"eventCount": 0, "previousText": "", "range": {"end": 0, "start": 0}, "target": 75, "text": "T"}
LOG  Event: change {"eventCount": 1, "target": 75, "text": "T"}
LOG  Event: changeText T
LOG  Event: selectionChange {"selection": {"end": 1, "start": 1}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 11}, "target": 75}

LOG  Event: keyPress {"eventCount": 1, "key": "e", "target": 75}
LOG  Event: textInput {"eventCount": 1, "previousText": "T", "range": {"end": 1, "start": 1}, "target": 75, "text": "e"}
LOG  Event: change {"eventCount": 2, "target": 75, "text": "Te"}
LOG  Event: changeText Te
LOG  Event: selectionChange {"selection": {"end": 2, "start": 2}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 19}, "target": 75}

LOG  Event: keyPress {"eventCount": 2, "key": "s", "target": 75}
LOG  Event: textInput {"eventCount": 2, "previousText": "Te", "range": {"end": 2, "start": 2}, "target": 75, "text": "s"}
LOG  Event: change {"eventCount": 3, "target": 75, "text": "Tes"}
LOG  Event: changeText Tes
LOG  Event: selectionChange {"selection": {"end": 3, "start": 3}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 28}, "target": 75}

LOG  Event: keyPress {"eventCount": 3, "key": "t", "target": 75}
LOG  Event: textInput {"eventCount": 3, "previousText": "Tes", "range": {"end": 3, "start": 3}, "target": 75, "text": "t"}
LOG  Event: change {"eventCount": 4, "target": 75, "text": "Test"}
LOG  Event: changeText Test
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 34}, "target": 75}

LOG  Event: submitEditing {"eventCount": 4, "target": 75, "text": "Test"}
LOG  Event: endEditing {"eventCount": 4, "target": 75, "text": "Test"}
LOG  Event: blur {"eventCount": 4, "target": 75, "text": "Test"}

Android:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 0, "locationX": 80.36363983154297, "locationY": 29.090909957885742, "pageX": 100.36363983154297, "pageY": 129.09091186523438, "target": 53, "targetSurface": -1, "timestamp": 107060200, "touches": [[Circular]]}
LOG  Event: focus {"target": 53}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 0, "locationX": 80.36363983154297, "locationY": 29.090909957885742, "pageX": 100.36363983154297, "pageY": 129.09091186523438, "target": 53, "targetSurface": -1, "timestamp": 107060255, "touches": []}

LOG  Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 53, "text": "T"}
LOG  Event: change {"eventCount": 2, "target": 53, "text": "T"}
LOG  Event: changeText T
LOG  Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 53, "text": "T"}
LOG  Event: selectionChange {"selection": {"end": 1, "start": 1}}
LOG  Event: keyPress {"key": "T"}

LOG  Event: textInput {"previousText": "T", "range": {"end": 1, "start": 0}, "target": 53, "text": "Te"}
LOG  Event: change {"eventCount": 4, "target": 53, "text": "Te"}
LOG  Event: changeText Te
LOG  Event: textInput {"previousText": "T", "range": {"end": 1, "start": 0}, "target": 53, "text": "Te"}
LOG  Event: selectionChange {"selection": {"end": 2, "start": 2}}
LOG  Event: keyPress {"key": "e"}

LOG  Event: textInput {"previousText": "Te", "range": {"end": 2, "start": 0}, "target": 53, "text": "Tes"}
LOG  Event: change {"eventCount": 6, "target": 53, "text": "Tes"}
LOG  Event: changeText Tes
LOG  Event: textInput {"previousText": "Te", "range": {"end": 2, "start": 0}, "target": 53, "text": "Tes"}
LOG  Event: selectionChange {"selection": {"end": 3, "start": 3}}
LOG  Event: keyPress {"key": "s"}

LOG  Event: textInput {"previousText": "Tes", "range": {"end": 3, "start": 0}, "target": 53, "text": "Test"}
LOG  Event: change {"eventCount": 8, "target": 53, "text": "Test"}
LOG  Event: changeText Test
LOG  Event: textInput {"previousText": "Tes", "range": {"end": 3, "start": 0}, "target": 53, "text": "Test"}
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}
LOG  Event: keyPress {"key": "t"}

LOG  Event: submitEditing {"target": 53, "text": "Test"}
LOG  Event: blur {"target": 53}
LOG  Event: endEditing {"target": 53, "text": "Test"}

Notes:

  • Sequence of events on focusing both platforms is the same: pressIn - focus - pressOut
  • Sequence of events on pressing Enter on both platforms is slightly:
    • iOS: submitEditing - endEditing - blur
    • Android: submitEditing - blur - endEditing
  • Sequence of events of typing is different:
    • iOS: keyPress - textInput - change - changeText - selectionChange - contentSizeChange
    • Android: textInput - change - changeText - textInput - selectionChange - keyPress
  • The submitEditing event is only emitted if user presses done/next/etc field, but not in case of tapping on another focusable element
  • In case of unmanaged TextInput (i.e. no value prop being passed) the events are the same on Android (TODO: confirm on iOS)

Mutli-Line - basic flow

RN version: 0.71.6

Setup:

<TextInput
  style={styles.textInput}
  value={value}
  mutliline={true}
  onChangeText={handleChangeText}
  onPressIn={handlePressIn}
  onPressOut={handlePressOut}
  onFocus={handleFocus}
  onBlur={handleBlur}
  onChange={handleChange}
  onEndEditing={handleEndEditing}
  onSubmitEditing={handleSubmitEditing}
  onKeyPress={handleKeyPress}
  onTextInput={handleTextInput}
  onSelectionChange={handleSelectionChange}
  onContentSizeChange={handleContentSizeChange}
/>

Steps:

  1. Press on the text input
  2. Type "AB\nCD" using on screen keyboard
  3. Press "return" to close the keyboard

Output:

iOS:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 1, "locationX": 244.66665649414062, "locationY": 14.333328247070312, "pageX": 264.6666564941406, "pageY": 125.33332824707031, "target": 75, "timestamp": 1014407822.0294167, "touches": [[Circular]]}
LOG  Event: focus {"eventCount": 0, "target": 75, "text": ""}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 244.66665649414062, "locationY": 14.333328247070312, "pageX": 264.6666564941406, "pageY": 125.33332824707031, "target": 75, "timestamp": 1014407901.1994168, "touches": []}

LOG  Event: keyPress {"eventCount": 0, "key": "A", "target": 75}
LOG  Event: textInput {"eventCount": 0, "previousText": "", "range": {"end": 0, "start": 0}, "target": 75, "text": "A"}
LOG  Event: change {"eventCount": 1, "target": 75, "text": "A"}
LOG  Event: changeText A
LOG  Event: selectionChange {"selection": {"end": 1, "start": 1}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 11.666666666666666}, "target": 75}

LOG  Event: keyPress {"eventCount": 1, "key": "B", "target": 75}
LOG  Event: textInput {"eventCount": 1, "previousText": "A", "range": {"end": 1, "start": 1}, "target": 75, "text": "B"}
LOG  Event: change {"eventCount": 2, "target": 75, "text": "AB"}
LOG  Event: changeText AB
LOG  Event: selectionChange {"selection": {"end": 2, "start": 2}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 23.333333333333332}, "target": 75}

// Enter key
LOG  Event: keyPress {"eventCount": 2, "key": "Enter", "target": 75}
LOG  Event: textInput {"eventCount": 2, "previousText": "AB", "range": {"end": 2, "start": 2}, "target": 75, "text": "\n"}
LOG  Event: change {"eventCount": 3, "target": 75, "text": "AB\n"}
LOG  Event: changeText AB
LOG  Event: selectionChange {"selection": {"end": 3, "start": 3}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 43, "width": 23.333333333333332}, "target": 75}

LOG  Event: keyPress {"eventCount": 3, "key": "C", "target": 75}
LOG  Event: textInput {"eventCount": 3, "previousText": "AB\n", "range": {"end": 3, "start": 3}, "target": 75, "text": "C"}
LOG  Event: change {"eventCount": 4, "target": 75, "text": "AB\nC"}
LOG  Event: changeText AB\nC
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}, "target": 75}

LOG  Event: keyPress {"eventCount": 4, "key": "D", "target": 75}
LOG  Event: textInput {"eventCount": 4, "previousText": "AB
C", "range": {"end": 4, "start": 4}, "target": 75, "text": "D"}
LOG  Event: change {"eventCount": 5, "target": 75, "text": "AB\nCD"}
LOG  Event: changeText AB\nCD
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 43, "width": 25.333333333333332}, "target": 75}

LOG  Event: endEditing {"eventCount": 5, "target": 75, "text": "AB\nCD"}
LOG  Event: blur {"eventCount": 5, "target": 75, "text": "AB\nCD"}

Android:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 0, "locationX": 257.4545593261719, "locationY": 33.09090805053711, "pageX": 277.4545593261719, "pageY": 133.09091186523438, "target": 53, "targetSurface": -1, "timestamp": 168170420, "touches": [[Circular]]}
LOG  Event: focus {"target": 53}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 0, "locationX": 257.4545593261719, "locationY": 33.818180084228516, "pageX": 277.4545593261719, "pageY": 133.81817626953125, "target": 53, "targetSurface": -1, "timestamp": 168170514, "touches": []}

LOG  Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 53, "text": "A"}
LOG  Event: change {"eventCount": 2, "target": 53, "text": "A"}
LOG  Event: changeText A
LOG  Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 53, "text": "A"}
LOG  Event: selectionChange {"selection": {"end": 1, "start": 1}}
LOG  Event: keyPress {"key": "A"}

LOG  Event: textInput {"previousText": "A", "range": {"end": 1, "start": 0}, "target": 53, "text": "AB"}
LOG  Event: change {"eventCount": 4, "target": 53, "text": "AB"}
LOG  Event: changeText AB
LOG  Event: textInput {"previousText": "A", "range": {"end": 1, "start": 0}, "target": 53, "text": "AB"}
LOG  Event: selectionChange {"selection": {"end": 2, "start": 2}}
LOG  Event: keyPress {"key": "B"}

// Enter
LOG  Event: textInput {"previousText": "AB", "range": {"end": 2, "start": 0}, "target": 53, "text": "AB\n"}
LOG  Event: contentSizeChange {"contentSize": {"height": 61.45454406738281, "width": 352.7272644042969}, "target": 53}
LOG  Event: change {"eventCount": 6, "target": 53, "text": "AB\n"}
LOG  Event: changeText AB
LOG  Event: textInput {"previousText": "AB", "range": {"end": 2, "start": 0}, "target": 53, "text": "AB\n"}
LOG  Event: selectionChange {"selection": {"end": 3, "start": 3}}

LOG  Event: textInput {"previousText": "", "range": {"end": 3, "start": 3}, "target": 53, "text": "C"}
LOG  Event: change {"eventCount": 8, "target": 53, "text": "AB\nC"}
LOG  Event: changeText AB\nC
LOG  Event: textInput {"previousText": "", "range": {"end": 3, "start": 3}, "target": 53, "text": "C"}
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}
LOG  Event: keyPress {"key": "C"}

LOG  Event: textInput {"previousText": "C", "range": {"end": 4, "start": 3}, "target": 53, "text": "CD"}
LOG  Event: change {"eventCount": 10, "target": 53, "text": "AB\nCD"}
LOG  Event: changeText AB\nCD
LOG  Event: textInput {"previousText": "C", "range": {"end": 4, "start": 3}, "target": 53, "text": "CD"}
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}}
LOG  Event: keyPress {"key": "D"}

LOG  Event: blur {"target": 53}
LOG  Event: endEditing {"target": 53, "text": "AB\nCD"}

Notes:

  • Sequence of events on focusing both platforms is the same: pressIn - focus - pressOut
  • Sequence of events on pressing Enter on both platforms is slightly:
    • iOS: endEditing - blur
    • Android: blur - endEditing
  • Sequence of events of typing is different:
    • iOS: keyPress - textInput - change - changeText - selectionChange - contentSizeChange (when it actually changes)
    • Android: textInput - contentSizeChange (only for Enter) - change - changeText - textInput - selectionChange - keyPress (not for Enter)
  • The submitEditing event is only emitted if user presses done/next/etc field, but not in case of tapping on another focusable element

Impact of editable={false} prop

Setup:

<TextInput
  style={styles.textInput}
  value={value}
  onChangeText={handleChangeText}
  editable={false}
  onPressIn={handlePressIn}
  onPressOut={handlePressOut}
  onFocus={handleFocus}
  onBlur={handleBlur}
  onChange={handleChange}
/>

Steps:

  1. Press TextInput

Output:

iOS:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 1, "locationX": 57.33332824707031, "locationY": 35, "pageX": 77.33332824707031, "pageY": 146, "target": 117, "timestamp": 707054389.9047917, "touches": [[Circular]]}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 57.33332824707031, "locationY": 35, "pageX": 77.33332824707031, "pageY": 146, "target": 117, "timestamp": 707054477.4037917, "touches": []}

Android:

(No events logged)

Managed TextInput rejecting changes

RN version: 0.71.6

Setup:

<TextInput
  style={styles.textInput}
  value="AAAA"
  editable={true}
  onPressIn={handlePressIn}
  onPressOut={handlePressOut}
  onFocus={handleFocus}
  onBlur={handleBlur}
  onChange={handleChange}
  onChangeText={handleChangeText}
  onSubmitEditing={handleSubmitEditing}
  onKeyPress={handleKeyPress}
  onTextInput={handleTextInput}
  onSelectionChange={handleSelectionChange}
  onContentSizeChange={handleContentSizeChange}
/>

Steps:

  1. Press on the text input
  2. Type "Test" using on screen keyboard
  3. Press "return" to close the keyboard

Output:

iOS:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 1, "locationX": 107, "locationY": 12, "pageX": 127, "pageY": 123, "target": 75, "timestamp": 967401529.229625, "touches": [[Circular]]}
LOG  Event: focus {"eventCount": 0, "target": 75, "text": "AAAA"}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 107, "locationY": 12, "pageX": 127, "pageY": 123, "target": 75, "timestamp": 967401608.349625, "touches": []}

LOG  Event: keyPress {"eventCount": 0, "key": "t", "target": 75}
LOG  Event: textInput {"eventCount": 0, "previousText": "AAAA", "range": {"end": 4, "start": 4}, "target": 75, "text": "t"}
LOG  Event: change {"eventCount": 1, "target": 75, "text": "AAAAt"}
LOG  Event: changeText AAAAt
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 52.333333333333336}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 46.666666666666664}, "target": 75}

LOG  Event: keyPress {"eventCount": 1, "key": "e", "target": 75}
LOG  Event: textInput {"eventCount": 1, "previousText": "AAAA", "range": {"end": 4, "start": 4}, "target": 75, "text": "e"}
LOG  Event: change {"eventCount": 2, "target": 75, "text": "AAAAe"}
LOG  Event: changeText AAAAe
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 56}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 46.666666666666664}, "target": 75}

LOG  Event: keyPress {"eventCount": 2, "key": "s", "target": 75}
LOG  Event: textInput {"eventCount": 2, "previousText": "AAAA", "range": {"end": 4, "start": 4}, "target": 75, "text": "s"}
LOG  Event: change {"eventCount": 3, "target": 75, "text": "AAAAs"}
LOG  Event: changeText AAAAs
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 55.666666666666664}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 46.666666666666664}, "target": 75}

LOG  Event: keyPress {"eventCount": 3, "key": "t", "target": 75}
LOG  Event: textInput {"eventCount": 3, "previousText": "AAAA", "range": {"end": 4, "start": 4}, "target": 75, "text": "t"}
LOG  Event: change {"eventCount": 4, "target": 75, "text": "AAAAt"}
LOG  Event: changeText AAAAt
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 52.333333333333336}, "target": 75}
LOG  Event: contentSizeChange {"contentSize": {"height": 21.666666666666668, "width": 46.666666666666664}, "target": 75}

LOG  Event: submitEditing {"eventCount": 4, "target": 75, "text": "AAAA"}
LOG  Event: blur {"eventCount": 4, "target": 75, "text": "AAAA"}

Android:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 0, "locationX": 94.18181610107422, "locationY": 22.18181800842285, "pageX": 114.18181610107422, "pageY": 122.18181610107422, "target": 53, "targetSurface": -1, "timestamp": 91073165, "touches": [[Circular]]}
LOG  Event: focus {"target": 53}

// `selectionChange` only happens on first focus. Subsequent focuses skip it.
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}

LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 0, "locationX": 94.18181610107422, "locationY": 22.18181800842285, "pageX": 114.18181610107422, "pageY": 122.18181610107422, "target": 53, "targetSurface": -1, "timestamp": 91073239, "touches": []}

LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAT"}
LOG  Event: change {"eventCount": 2, "target": 53, "text": "AAAAT"}
LOG  Event: changeText AAAAT
LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAT"}
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}}
LOG  Event: keyPress {"key": "T"}
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}

LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAe"}
LOG  Event: change {"eventCount": 4, "target": 53, "text": "AAAAe"}
LOG  Event: changeText AAAAe
LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAe"}
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}}
LOG  Event: keyPress {"key": "e"}
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}

LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAs"}
LOG  Event: change {"eventCount": 6, "target": 53, "text": "AAAAs"}
LOG  Event: changeText AAAAs
LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAs"}
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}}
LOG  Event: keyPress {"key": "s"}
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}

LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAt"}
LOG  Event: change {"eventCount": 8, "target": 53, "text": "AAAAt"}
LOG  Event: changeText AAAAt
LOG  Event: textInput {"previousText": "AAAA", "range": {"end": 4, "start": 0}, "target": 53, "text": "AAAAt"}
LOG  Event: selectionChange {"selection": {"end": 5, "start": 5}}
LOG  Event: keyPress {"key": "t"}
LOG  Event: selectionChange {"selection": {"end": 4, "start": 4}}

LOG  Event: submitEditing {"target": 53, "text": "AAAA"}
LOG  Event: blur {"target": 53}

Using backspace

Android:

Event: pressIn {"changedTouches": [[Circular]], "identifier": 0, "locationX": 54.54545593261719, "locationY": 20, "pageX": 74.54545593261719, "pageY": 120, "target": 83, "targetSurface": -1, "timestamp": 542793523, "touches": [[Circular]]}
Event: focus {"target": 83}
Event: pressOut {"changedTouches": [[Circular]], "identifier": 0, "locationX": 54.54545593261719, "locationY": 20, "pageX": 74.54545593261719, "pageY": 120, "target": 83, "targetSurface": -1, "timestamp": 542793571, "touches": []}

Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 83, "text": "A"}
Event: change {"eventCount": 2, "target": 83, "text": "A"}
Event: changeText A
Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 83, "text": "A"}
Event: selectionChange {"selection": {"end": 1, "start": 1}}
Event: keyPress {"key": "A"}

Event: textInput {"previousText": "A", "range": {"end": 1, "start": 0}, "target": 83, "text": "Ab"}
Event: change {"eventCount": 4, "target": 83, "text": "Ab"}
Event: changeText Ab
Event: textInput {"previousText": "A", "range": {"end": 1, "start": 0}, "target": 83, "text": "Ab"}
Event: selectionChange {"selection": {"end": 2, "start": 2}}
Event: keyPress {"key": "b"}

Event: textInput {"previousText": "Ab", "range": {"end": 2, "start": 0}, "target": 83, "text": "A"}
Event: change {"eventCount": 6, "target": 83, "text": "A"}
Event: changeText A
Event: textInput {"previousText": "Ab", "range": {"end": 2, "start": 0}, "target": 83, "text": "A"}
Event: selectionChange {"selection": {"end": 1, "start": 1}}
Event: keyPress {"key": "Backspace"}

Event: textInput {"previousText": "A", "range": {"end": 1, "start": 0}, "target": 83, "text": ""}
Event: change {"eventCount": 8, "target": 83, "text": ""}
Event: changeText 
Event: textInput {"previousText": "A", "range": {"end": 1, "start": 0}, "target": 83, "text": ""}
Event: selectionChange {"selection": {"end": 0, "start": 0}}
Event: keyPress {"key": "Backspace"}
Event: contentSizeChange {"contentSize": {"height": 45.818180084228516, "width": 352.7272644042969}, "target": 83}

Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 83, "text": "C"}
Event: contentSizeChange {"contentSize": {"height": 40.3636360168457, "width": 381316.375}, "target": 83}
Event: change {"eventCount": 10, "target": 83, "text": "C"}
Event: changeText C
Event: textInput {"previousText": "", "range": {"end": 0, "start": 0}, "target": 83, "text": "C"}
Event: selectionChange {"selection": {"end": 1, "start": 1}}
Event: keyPress {"key": "C"}

Event: submitEditing {"target": 83, "text": "C"}
Event: blur {"target": 83}
Event: endEditing {"target": 83, "text": "C"}

Micro & Macro tasks after events:

Initial press

iOS

Event: pressIn Object {
  "changedTouches": Array [
    [Circular],
  ],
  "identifier": 1,
  "locationX": 89.33332824707031,
  "locationY": 27.333328247070312,
  "pageX": 109.33332824707031,
  "pageY": 138.3333282470703,
  "target": 149,
  "timestamp": 233880.94159499998,
  "touches": Array [
    [Circular],
  ],
}
 - pressIn (microtask)
 - pressIn (macrotask)
Event: focus Object {
  "eventCount": 0,
  "target": 149,
  "text": "",
}
 - focus (microtask)
 - focus (macrotask)
Event: pressOut Object {
  "changedTouches": Array [
    [Circular],
  ],
  "identifier": 1,
  "locationX": 89.33332824707031,
  "locationY": 27.333328247070312,
  "pageX": 109.33332824707031,
  "pageY": 138.3333282470703,
  "target": 149,
  "timestamp": 233883.208488,
  "touches": Array [],
}
 - pressOut (microtask)
 - pressOut (macrotask)

Android

Event: pressIn Object {
  "changedTouches": Array [
    [Circular],
  ],
  "identifier": 0,
  "locationX": 29.997209548950195,
  "locationY": 29.129465103149414,
  "pageX": 49.99721145629883,
  "pageY": 129.1294708251953,
  "target": 53,
  "targetSurface": -1,
  "timestamp": 109739730,
  "touches": Array [
    [Circular],
  ],
}
 - pressIn (microtask)
 - pressIn (macrotask)
Event: pressOut Object {
  "changedTouches": Array [
    [Circular],
  ],
  "identifier": 0,
  "locationX": 29.997209548950195,
  "locationY": 29.129465103149414,
  "pageX": 49.99721145629883,
  "pageY": 129.1294708251953,
  "target": 53,
  "targetSurface": -1,
  "timestamp": 109739916,
  "touches": Array [],
}
 - pressOut (microtask)
 - pressOut (macrotask)
Event: focus Object {
  "target": 53,
}
 - focus (microtask)
 - focus (macrotask)

Notes:

  • each of the initial events: pressIn, focus, pressOut yields for micro and macro task resolution after the event.

Typing

iOS

Event: keyPress Object {
  "eventCount": 2,
  "key": "A",
  "target": 149,
}
 - keyPress (microtask)
 - keyPress (macrotask)
Event: textInput Object {
  "eventCount": 2,
  "previousText": "",
  "range": Object {
    "end": 0,
    "start": 0,
  },
  "target": 149,
  "text": "A",
}
 - textInput (microtask)
 - textInput (macrotask)
Event: change Object {
  "eventCount": 3,
  "target": 149,
  "text": "A",
}
Event: changeText A
 - change (microtask)
 - changeText (microtask)
Event: selectionChange Object {
  "selection": Object {
    "end": 1,
    "start": 1,
  },
  "target": 149,
}
 - selectionChange (microtask)
 - change (macrotask)
 - changeText (macrotask)
 - selectionChange (macrotask)

Android

Event: textInput Object {
  "previousText": "",
  "range": Object {
    "end": 0,
    "start": 0,
  },
  "target": 53,
  "text": "A",
}
 - textInput (microtask)

Event: change Object {
  "eventCount": 2,
  "target": 53,
  "text": "A",
}

Event: changeText A
 - change (microtask)
 - changeText (microtask)

Event: textInput Object {
  "previousText": "",
  "range": Object {
    "end": 0,
    "start": 0,
  },
  "target": 53,
  "text": "A",
}
 - textInput (microtask)

Event: selectionChange Object {
  "selection": Object {
    "end": 1,
    "start": 1,
  },
}

 - selectionChange (microtask)
 - textInput (macrotask)
 - change (macrotask)
 - changeText (macrotask)
 - textInput (macrotask)
 - selectionChange (macrotask)

Notes:

  • each of the events: keyPress, textInput, etc, yields for micro task resolution after the event.
  • macro task resolution on iOS grouped into following groups:
    • keyPress
    • textInput
    • change + changeText + selectionChange
  • macro task resolution on Android is grouped into single group

Clearing TextInput

Scenario:

  1. TextInput Events experiment
  2. Fill in TextInput with Random text, focus out
  3. Start recording events
  4. Enter text field
  5. Select all text using context menu
  6. Press backspace
  7. Exit TextInput

iOS

// Enter
Event: pressIn {"changedTouches": [[Circular]], "identifier": 1, "locationX": 59.5, "locationY": 6, "pageX": 79.5, "pageY": 90, "target": 75, "timestamp": 166484128.726374, "touches": [[Circular]]}
Event: focus {"eventCount": 23, "target": 75, "text": "Test"}
Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 59.5, "locationY": 6, "pageX": 79.5, "pageY": 90, "target": 75, "timestamp": 166484183.03356, "touches": []}

// Select all (Cmd+A)
Event: selectionChange {"selection": {"end": 4, "start": 0}, "target": 75}

// Press backspace
Event: keyPress {"eventCount": 23, "key": "Backspace", "target": 75}
Event: textInput {"eventCount": 23, "previousText": "Test", "range": {"end": 4, "start": 0}, "target": 75, "text": ""}
Event: change {"eventCount": 24, "target": 75, "text": ""}
Event: changeText 
Event: selectionChange {"selection": {"end": 0, "start": 0}, "target": 75}
Event: contentSizeChange {"contentSize": {"height": 21.5, "width": 143.5}, "target": 75}

// Exit
Event: endEditing {"eventCount": 24, "target": 75, "text": ""}
Event: blur {"eventCount": 24, "target": 75, "text": ""}

iOS (clear button)

Event: pressIn {"changedTouches": [[Circular]], "identifier": 1, "locationX": 321, "locationY": 19.5, "pageX": 341, "pageY": 103.5, "target": 75, "timestamp": 166027702.319404, "touches": [[Circular]]}
Event: keyPress {"eventCount": 18, "key": "Backspace", "target": 75}
Event: textInput {"eventCount": 18, "previousText": "", "range": {"end": 4, "start": 0}, "target": 75, "text": ""}
Event: selectionChange {"selection": {"end": 0, "start": 0}, "target": 75}
Event: contentSizeChange {"contentSize": {"height": 21.5, "width": 143.5}, "target": 75}
Event: change {"eventCount": 19, "target": 75, "text": ""}
Event: changeText 
Event: selectionChange {"selection": {"end": 0, "start": 0}, "target": 75}
Event: focus {"eventCount": 19, "target": 75, "text": ""}
Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 321, "locationY": 19.5, "pageX": 341, "pageY": 103.5, "target": 75, "timestamp": 166027788.429719, "touches": []}

Android

// Enter
Event: pressIn {"changedTouches": [[Circular]], "identifier": 0, "locationX": 18.172496795654297, "locationY": 24.707475662231445, "pageX": 38.1724967956543, "pageY": 124.70747375488281, "target": 53, "targetSurface": -1, "timestamp": 216026, "touches": [[Circular]]}
Event: focus {"target": 53}
Event: pressOut {"changedTouches": [[Circular]], "identifier": 0, "locationX": 18.172496795654297, "locationY": 24.707475662231445, "pageX": 38.1724967956543, "pageY": 124.70747375488281, "target": 53, "targetSurface": -1, "timestamp": 216070, "touches": []}

// Select all
Event: selectionChange {"selection": {"end": 4, "start": 0}}

// Press backspace
Event: textInput {"previousText": "Test", "range": {"end": 4, "start": 0}, "target": 53, "text": ""}
Event: change {"eventCount": 20, "target": 53, "text": ""}
Event: changeText 
Event: textInput {"previousText": "Test", "range": {"end": 4, "start": 0}, "target": 53, "text": ""}
Event: selectionChange {"selection": {"end": 0, "start": 0}}
Event: contentSizeChange {"contentSize": {"height": 45.818180084228516, "width": 352.7272644042969}, "target": 53}

// Exit
Event: blur {"target": 53}
Event: endEditing {"target": 53, "text": ""}

User Event design

// Step 1: enter
- focus (skip pressIn/pressOut in sync with RTL UserEvent clear())

// Step 2: select all
- selectionChange: whole text

// Step 3: clear with backspace key (in sync with type())
- keyPress: backspace
- textInput: "" & "previous text" (for multiline only)
- change: ""
- changeText: ""
- selectionChange: zero range
- contentSizeChange: (for multiline-only)

// Step 4: exit
- blur