How can I “undo” the programmatic insertion of text into a textarea?

后端 未结 12 653
失恋的感觉
失恋的感觉 2020-12-17 09:27

I have a textarea and a button. Clicking the button causes text to be inserted into the textarea.

Is there a way to allow a user to press Ctrl/Cmd+z to undo the inse

相关标签:
12条回答
  • 2020-12-17 10:13

    You can do like this,

    HTML

    <div id="text-content">
     <textarea rows="10" cols="50"></textarea>
       <input type="button" name="Insert" value="Insert" />
    </div>
    

    Jquery

    var oldText = [],ctrl=false;
       $(function(){
          $("input[name=Insert]").click(function(){
              var text = oldText.length+1;
              oldText.push(text);
              var textAreaText = $('textarea').val();
              textAreaText +=text;
              $('textarea').val(textAreaText);
              $("input[name=insertText]").val('');
         });
         $('textarea').keydown(function(e){
                if(e.which == 17){ ctrl = true;}
         });
         $('textarea').keyup(function(e){
             var c = e.which;
             if(c == 17){ ctrl = false;}
             if(ctrl==true && c==90 ){
                oldText.pop(); 
                var text = '';
                $.each(oldText,function(i,ele){
                   text += ele;
                });
                $('textarea').val(text);
            }
         })
      })
    

    You can also check and experiment with the fiddle.

    0 讨论(0)
  • 2020-12-17 10:15

    Here's a short and non-jQuery solution which works in Firefox too (undo isn't possible in any way there)

    // Solution
    function insertText(textarea, text) {
    	// https://stackoverflow.com/a/55174581/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.

    0 讨论(0)
  • 2020-12-17 10:21

    I think the easiest approach to leverage browser's undo stack instead of capturing events.

    To do this you need to use different codes for different browsers. Luckily out of all major browsers only Firefox has a different approach.

    // http://stackoverflow.com/a/9851769/529024
    // Opera 8.0+
    var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
    
    // Firefox 1.0+
    var isFirefox = typeof InstallTrigger !== 'undefined';
    
    // Safari 3.0+ "[object HTMLElementConstructor]" 
    var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 || (function(p) {
      return p.toString() === "[object SafariRemoteNotification]";
    })(!window['safari'] || safari.pushNotification);
    
    // Internet Explorer 6-11
    var isIE = /*@cc_on!@*/ false || !!document.documentMode;
    
    // Edge 20+
    var isEdge = !isIE && !!window.StyleMedia;
    
    // Chrome 1+
    var isChrome = !!window.chrome && !!window.chrome.webstore;
    
    
    
    var position = 0;
    
    // text to anser
    var text = 'Inserted Text';
    
    // Just for fun :)
    if (isFirefox)
      text = "  __ Firefox __ ";
    else if (isIE)
      text = "  __ IE __ ";
    else if (isEdge)
      text = "  __ Edge __ ";
    else if (isSafari)
      text = "  __ Safari __ ";
    else if (isOpera)
      text = "  __ Opera __ ";
    else if (isChrome)
      text = "  __ Chrome __ ";
    
    /* Adding text on click based on browser */
    jQuery(".addText").on("click", function() {
      if (isFirefox) {
        // Firefox
        var val = jQuery(".textArea").val();
    
        var firstText = val.substring(0, position);
        var secondText = val.substring(position);
    
        jQuery(".textArea").val(firstText + text + secondText);
      } else {
    
        jQuery(".textArea").focus();
        var val = jQuery(".textArea").val();
    
        jQuery(".textArea")[0].selectionStart = position;
        jQuery(".textArea")[0].selectionEnd = position;
    
        document.execCommand('insertText', false, text);
      }
    });
    
    jQuery(".textArea").on("focusout", function(e) {
      position = jQuery(this)[0].selectionStart;
    });
    textarea {
      padding: 10px;
      font-family: Calibri;
      font-size: 18px;
      line-height: 1.1;
      resize: none;
    }
    .addText {
      padding: 5px 15px;
      transition: all 0.5s;
      border: 1px solid black;
      border-radius: 2px;
      background-color: #169c16;
      width: 70px;
      margin: 10px 0;
      color: white;
      cursor: pointer;
    }
    .addText:hover {
      background-color: #2776b9;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <textarea name='textArea' class='textArea' rows="7" cols="50">Suspendisse convallis, metus at laoreet congue, sapien dui ornare magna, a porttitor felis purus a ipsum. Morbi vulputate erat rhoncus, luctus neque ut, lacinia orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis
      egestas. Fusce aliquam, nulla nec fringilla ultrices, ipsum lectus maximus nisl, ut laoreet purus lectus eget nisl. Duis blandit cursus nulla. Vestibulum consectetur, nunc non viverra condimentum, neque neque tincidunt diam, nec vestibulum neque nisi
      ac sem. Integer aliquam a leo id laoreet. Mauris ultrices mauris lorem, eu hendrerit odio volutpat ut. Nam eget hendrerit metus.</textarea>
    <div class='addText'>
      Add Text
    </div>

    Tested on:

    • FF: 50
    • Chrome: 55
    • Edge 38
    • Safari: 5.1
    • Opera: 42
    0 讨论(0)
  • 2020-12-17 10:22

    Concrete libs and technologies strongly depends on your stack.

    There are 2 general ways i can think of instantly.

    First: Save the previous state in your controller. Hook into the shortcut and replace the content to the previous state. If other modifications are made delete the hook. More or less your 2013 approach

    This is a quick way and does not play nice if you want a stack with more than a one time edit history.

    Second: Watch the textinput and save the state on a stack periodicly. Hook into the shortcut. (Take over the whole process). This is a cleaner way because your modification is conceptually the same as user modifications.

    This could be pretty straightforward with a redux/flux architecture http://redux.js.org/docs/recipes/ImplementingUndoHistory.html

    For capturing cmd/ctrl+z you could look into https://github.com/madrobby/keymaster

    If you elaborate a bit more about your stack/requirements i would be happy to expand this answer.

    0 讨论(0)
  • 2020-12-17 10:22

    There are alot of applicable answers here that would work for what you want. Here is what I would do in your circumstance. This allows for the changing of a text variable that you would most likely use, you can set it or the user can set it with another field, etc.

    codepen here

    the jist of it would be something like this.

    $(document).ready(function(){
      function deployText(){
        var textArray = [];
        var textToAdd = 'let\'s go ahead and add some more text';
        var textarea = $('textarea');
        var origValue = textarea.text();
        textArray.push(origValue);
    
        $('button').on('click', function(e){
          textArray.push(textToAdd);
          textarea.text(textArray);
          console.log(textArray.length);
          console.log(textArray);
        });
    
        $(document).on('keypress', function(e){
          var zKey = 26;
          if(e.ctrlKey && e.which === zKey){
            removePreviousText();
          }
        })
    
        function removePreviousText(){
          console.log(textArray);
          if(textArray.length > 1){
            textArray.pop();
            $('textarea').text(textArray);
          }
        }
      }
      deployText()
    })
    
    0 讨论(0)
  • 2020-12-17 10:26

    Even if this question is a year ago old, I want to share my way too.

    You can do it like this:

        $(function () {
    
      // Check to see if an array is not already defined.
      if (!$('#t').data('old-val')) {
    
        // If the check returns True we proceed to create an array in old-val.
        $('#t').data('old-val', []);
    
      }
    
      // Get the current content value.
      inputValue = $('#t').val();
    
      // Push it to the old-val array.
      $('#t').data('old-val').push(inputValue);
    
      // We start with a current array position of 0.
      curArrPos = 0;
    
    
      $('#c').click(function () {
        // Append a string to the #t.
        $('#t').val(' ==this is the 2nd appended text==');
    
    
        // Save the current content value.
        inputValue = $('#t').val();
        // Push it to the array.
        $('#t').data('old-val').push(inputValue);
        // Increment current array position.
        ++curArrPos;
    
      });
    
    
      $('#b').click(function () {
        // Append a string to the #t.
        $('#t').val(' ==this is the 1st appended text==');
    
    
        // Save the current content value.
        inputValue = $('#t').val();
        // Push it to the array.
        $('#t').data('old-val').push(inputValue);
        // Increment current array position.
        ++curArrPos;
    
      });
    
      $('#undo').click(function () {
        // First check that the old-val array length is greater than 1 (It's the initial position. No need undoing to a blank state) and current array position greater than 0 (for the same reason).
        if ($('#t').data('old-val').length > 1 && curArrPos > 0) {
    
          // Set current #t value to the one in the current array position, minus one.
          // Minus one gets you to the previous array position (ex. current=5; previous= current - 1 = 4).
          $('#t').val($('#t').data('old-val')[curArrPos - 1]);
    
          // Decrease current array position, because we effectively shifted back by 1 position.
          --curArrPos;
        }
      });
    
      $('#redo').click(function () {
        if (currentArrayPos < $('#c').data('old-val').length - 1) {
    
          $('#t').val($('#t').data('old-val')[curArrPos + 1]);
    
          // Increase current array position, because we effectively shifted forward by 1 position.
          ++curArrPos;
    
        }
      });
    
    });
    

    Here's a fiddle if you want to experiment with it http://jsfiddle.net/45Jwz/1/

    I wrote the code like this for good comprehension, but you should of course write the actual code better and less verbose than this.

    0 讨论(0)
提交回复
热议问题