22

Is there a way to edit the contents of an input or textarea with javascript, and have that change be undo-able with the browser's "undo" command (eg ctrl-Z)?

I am trying to insert a string, such as "Foo {0} bar", into the value at the selection, and if the user has selected a range, the selected range is inserted into the string in place of the "{0}".

For example if the textarea contains "Example 1 2 3" and the cursor is at "Example 1| 2 3", then the function would change the value to "Example 1Foo blah bar 2 3" (valueIfNothingSelected being "blah" in this case). If the range "1 2" was selected, the function would instead change the value to "Example Foo 1 2 bar 3".

In Chrome I tested this function out, and it does what it is supposed to, but I cannot reverse the change with undo.

function insertTextAtCursor(text, valueIfNothingSelected) {
    var field = $('textarea[name="task_log_description"]')[0];
    var startPos = field.selectionStart;
    var endPos = field.selectionEnd;
    var processedText;
    if (startPos == endPos) {
        processedText = text.replace('{0}', valueIfNothingSelected);
        field.value = field.value.substring(0, startPos) + processedText + field.value.substring(endPos, field.value.length);
        field.selectionStart = startPos + text.indexOf('{0}');
        field.selectionEnd = field.selectionStart + valueIfNothingSelected.length;
    } else {
        var selectedText = field.value.substring(startPos, endPos);
        processedText = text.replace('{0}', selectedText);
        field.value = field.value.substring(0, startPos) + processedText + field.value.substring(endPos, field.value.length);
        field.selectionStart = startPos + text.indexOf('{0}');
        field.selectionEnd = field.selectionStart + selectedText.length;
    }
    field.focus();
}
Mar
  • 7,765
  • 9
  • 48
  • 82
  • 1
    ctrl-z already works for me in Chrome+Firefox on Ubuntu 14.04. What's the situation where it's not working for you? – Jack Nov 19 '14 at 22:09
  • @Jack Expanded question to include my javascript method. For whatever reason, `Ctrl-z` doesn't work when this method is used. – Mar Nov 19 '14 at 22:18
  • possible duplicate of [Javascript textarea undo redo](http://stackoverflow.com/questions/7553430/javascript-textarea-undo-redo) – Fraser Nov 19 '14 at 22:34

3 Answers3

19

I found a solution that works at https://stackoverflow.com/a/10345596/1021426

By replacing the "field.value = ..." lines with:

document.execCommand("insertText", false, processedText);

...and moving "field.focus()" to before that line, I was able to achieve the undo/redo functionality I desired.

Community
  • 1
  • 1
Mar
  • 7,765
  • 9
  • 48
  • 82
  • it's funny how google works, this is the front result, not the one you linked to :D – krivar Aug 26 '18 at 13:38
  • 1
    https://stackoverflow.com/questions/60581285/execcommand-is-now-obsolete-whats-the-alternative – Lance Sep 19 '22 at 13:38
3

Edit: Firefox now supports execCommand too. You just need that.


Here's a solution which works in Firefox too, is the most performant one and offers undo functionality everywhere except Firefox (where it just isn't possible natively)

// Solution
function insertText(textarea, text) {
    // https://stackoverflow.com/a/55174561/288906
    if (!document.execCommand('insertText', false, text)) {
        textarea.setRangeText(text);
    }
}

// Demo code to add text on click
const textarea = document.querySelector('textarea');
textarea.onclick = () => insertText(
  textarea,
  '@'
);
<textarea>Click in here to add text</textarea>

I also packaged this up as an npm module, improving consistency across browsers and adding utilities like "insert", "replace", "search and replace", etc

fregante
  • 29,050
  • 14
  • 119
  • 159
-1

Here's code to replace the contents of a <textarea> instead of just inserting.

function setValueAndUpdateUndoStack(textarea, text) {
    // Must select all, so that the old text is deleted.
    textarea.focus();
    textarea.select();
    
    // https://stackoverflow.com/a/55174561/3480193
    if ( ! document.execCommand('insertText', false, text) ) {
        textarea.setRangeText(text);
    }
}
RedDragonWebDesign
  • 1,797
  • 2
  • 18
  • 24