Skip to content

TextInput Events

Maciej Jastrzebski edited this page Apr 29, 2023 · 38 revisions

Typical Flow

RN version: 0.71.6

Setup:

<TextInput
  style={styles.textInput}
  value={value}
  onChangeText={handleChangeText}
  editable={true}
  onPressIn={handlePressIn}
  onPressOut={handlePressOut}
  onFocus={handleFocus}
  onBlur={handleBlur}
  onChange={handleChange}
  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": 253, "locationY": 30.333328247070312, "pageX": 273, "pageY": 141.3333282470703, "target": 75, "timestamp": 875928682.0450834, "touches": [[Circular]]}
LOG  Event: focus {"eventCount": 0, "target": 75, "text": ""}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 1, "locationX": 253, "locationY": 30.333328247070312, "pageX": 273, "pageY": 141.3333282470703, "target": 75, "timestamp": 875928744.7620833, "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: blur {"eventCount": 4, "target": 75, "text": "Test"}

Android:

LOG  Event: pressIn {"changedTouches": [[Circular]], "identifier": 0, "locationX": 160, "locationY": 40.3636360168457, "pageX": 180, "pageY": 140.36363220214844, "target": 53, "targetSurface": -1, "timestamp": 10290805, "touches": [[Circular]]}
LOG  Event: focus {"target": 53}
LOG  Event: pressOut {"changedTouches": [[Circular]], "identifier": 0, "locationX": 160, "locationY": 40.3636360168457, "pageX": 180, "pageY": 140.36363220214844, "target": 53, "targetSurface": -1, "timestamp": 10290861, "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}

Notes:

  • Sequence of events on focusing both platforms is the same: pressIn - focus - pressOut
  • Sequence of events on pressing Enter on both platforms is the same: submitEditing - blur
  • 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, n case of tapping on another focusable element it is not emitted
  • In case of unmanaged TextInput (i.e. no value prop being passed) the events are the same on Android (TODO: confirm on iOS)

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}
Clone this wiki locally