Undo typing

Did you drop your phone again while typing? You cannot undo dropping your phone, but at least you can undo the typing you’ve done on your phone before breaking the back glass.

Undo Typing popup on iOS

But what actually happens in the DOM when you undo typing?

Test setup

Let’s create a textarea and capture all events to see what events are actually triggered on undo. Creating a textarea is easy enough, but how do we capture all events? There isn’t a addEventListener("*") for any event.

After some googling, some suggest iterating over all properties that start with on: onkeydown, onkeyup, etc. Let’s try that!

const textarea = document.createElement("textarea");
const pre = document.createElement("pre");
const eventNames = [];
for (const key in textarea) {
  if (key.substr(0, 2) === "on") {
console.log(eventNames); /* ["copy", "cut", "paste", "abort", "blur", "cancel", "canplay",
  "canplaythrough", "change", "click", "close", "contextmenu", "cuechange",
  "dblclick", "drag", "dragend", "dragenter", "dragleave", "dragover", "dragstart",
  "drop", "durationchange", "emptied", "ended", "error", "focus", "input", "invalid",
  "keydown", "keypress", "keyup", "load", "loadeddata", "loadedmetadata", "loadstart",
  "mousedown", "mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup",
  "mousewheel", "pause", "play", "playing", "progress", "ratechange", "reset", "resize",
  "scroll", "seeked", "seeking", "select", "stalled", "submit", "suspend", "timeupdate",
  "toggle", "volumechange", "waiting", "wheel", "auxclick", "gotpointercapture",
  "lostpointercapture", "pointerdown", "pointermove", "pointerup", "pointercancel",
  "pointerover", "pointerout", "pointerenter", "pointerleave", "selectstart", "selectionchange",
  "animationend", "animationiteration", "animationstart", "transitionend", "formdata",
  "pointerrawupdate", "beforecopy", "beforecut", "beforepaste", "search", "fullscreenchange",
  "fullscreenerror", "webkitfullscreenchange", "webkitfullscreenerror"] */

const eventCounts = {};
for (const eventName of eventNames) {
  textarea.addEventListener(eventName, (event) => {
    if (!eventCounts[eventName]) {
      eventCounts[eventName] = 0;
    console.log(eventName, event);
    pre.textContent = JSON.stringify(eventCounts, null, 2);

That’s a LOT of events. I never actually realised there were so many!

Testing this in my desktop browser, there seems to be an input event I didn’t know about. The input event is triggered (together with keyboard events) when using Ctrl + Z. Even when using the Cut feature from the Edit toolbar of the browser (I mean, who does that?), an input event is triggered, together with a cut event.

Now let’s try the undo feature on my phone: I enter some text, shake the phone, and press undo. Only an input event is triggered!

So what’s this InputEvent? Let’s see what Mozzila has to say:

Looks like there is all kinds of interesting data on the InputEvent interface.

Most notably: event.inputType is "historyUndo". Is this even a documented standard somewhere? Looking at InputEvent’s inputType specification at w3c, there is an editor’s draft containing a list of inputType values:

Both "historyUndo" and "historyRedo" are there. Cool.

So there’s that: When you undo typing on your browser, a special input event is triggered, with inputType = "historyUndo". Now you know.