5

I have an HTML input. When a user types in it, I've set up the 'input' event to handle updating the input to a filtered version of what the user typed (as well as updating selectionStart and selectionEnd for smooth UX). This happens constantly in order to give the proper effect.

What I've noticed, however, is that whenever JS sets the value of an input via input.value = '...';, it appears the undo history for the input disappears. That is, pressing Ctrl-Z with it focused no longer steps back to the previous state.

Is there any way to either provide the input custom undo history, or otherwise prevent it from losing the history whilst still changing its value?


Here is a minimal example of my issue:
After typing in the top input (which rudimentarily adds periods between every character), Ctrl-Z does not undo.

<body>
    <input type="text" id="textbox" placeholder="No undo"/><br/>
    <input type="text" id="textbox2" placeholder="Undo"/>
    <script>
        var tbx = document.getElementById("textbox");
        tbx.addEventListener('input', () => {
            tbx.value = tbx.value + '.'
        });
    </script>
</body>
Spectric
  • 30,714
  • 6
  • 20
  • 43
Codesmith
  • 5,779
  • 5
  • 38
  • 50
  • Related question: [Is it possible to edit a text input with javascript and add to the Undo stack?](https://stackoverflow.com/questions/27027833/is-it-possible-to-edit-a-text-input-with-javascript-and-add-to-the-undo-stack) – Jo Liss Sep 06 '22 at 20:51

1 Answers1

1

You can try storing the input's previous value in a variable, then listen for the Ctrl + Z key combination in a keydown event listener attached to the input. When it is fired, you can set the value of the input to the previous stored value.

btn.addEventListener('click', function() {
  savePrevInput(input.value)
  input.value = "Hello World!";
})

var prevInput;

function savePrevInput(input) {
  prevInput = input;
}

input.addEventListener("keydown", function(e) {
  if (e.ctrlKey && e.keyCode == 90) {
    if (prevInput) {
      input.value = prevInput;
      input.selectionStart = prevInput.length;
    }
  }
})
<input id="input" />

<button id="btn">Change</button>
Spectric
  • 30,714
  • 6
  • 20
  • 43
  • Yes, I considered this. Should I be concerned about any kind of compatibility issues with this? I'm on Windows and couldn't recall if Apple uses Cmd or Ctrl-Z for undo, and how that appears in the event. Also wasn't sure if the default undo shortcut could be remapped..? (or maybe that's excessive concern :-) – Codesmith Oct 09 '21 at 02:58
  • @Codesmith it should be OK. The regular Ctrl + Z behavior still works with this implementation. – Spectric Oct 09 '21 at 03:13
  • Ok, thanks! I was able to fully replace undo/redo by listening to 'keydown' and checking for Ctrl-Z, Ctrl-Y, and Ctrl-Shift-Z. I actually prevented default in my case. – Codesmith Oct 09 '21 at 03:43
  • Uncaught TypeError: Cannot read properties of undefined (reading 'length') – LuisAFK May 30 '22 at 16:52
  • 1
    @Spectric, LuisAFK is correct. I didn't actually use your code (I used an undo stack) but pressing ctrl-Z without hitting "Change" breaks it. – Codesmith Sep 08 '22 at 12:17
  • @Codesmith sorry, didn't notice that! I've fixed it. – Spectric Sep 08 '22 at 22:30