如何“撤消”将文本编程插入文本区域?

时间:2012-11-28 02:54:15

标签: javascript jquery textarea undo

我有一个textarea和一个按钮。单击该按钮可将文本插入文本区域。

有没有办法允许用户按Ctrl / Cmd + z撤消文本的插入并将textarea恢复到以前的状态?

12 个答案:

答案 0 :(得分:7)

我认为最简单的方法是利用浏览器的撤销堆栈而不是捕获事件。

为此,您需要为不同的浏览器使用不同的代码。幸运的是,在所有主流浏览器中,只有Firefox有不同的方法。



// 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>
&#13;
&#13;
&#13;

经过测试:

  • FF:50
  • Chrome:55
  • Edge 38
  • Safari:5.1
  • Opera:42

答案 1 :(得分:6)

您需要以特殊方式插入文本,以便用户可以使用正常的撤消/重做行为。

var textEvent = document.createEvent('TextEvent');

textEvent.initTextEvent('textInput', true, true, null, "new text");

document.getElementById("your-textarea").dispatchEvent(textEvent);

答案 2 :(得分:4)

textarea的原始值保存在data

var $textarea = $('textarea');

$('button').on('click', function () {
    var val = $textarea.val();

    $textarea.data('old-val', val).val(val + ' some text');
});

如果你想要一个数据数组(如@ahren建议的那样),请使用:

var $textarea = $('textarea');

$('button').on('click', function () {
    var val = $textarea.val();

    if ( ! $textarea.data('old-val')) {
        $textarea.data('old-val', []);
    }

    $textarea.data('old-val').push(val);

    $textarea.val(val + ' some text');
});

答案 3 :(得分:1)

$("#target").keypress(function(event) {
  if ( event.which == 'Z' && first press == 'Cmd' && second press == 'Ctrl') {//check for key press
     event.preventDefault();
    text.value = defaultValueForTextField
   }
});

那应该是你要找的东西。第一次和第二次按下需要保存,因为您需要组合印刷机。您确实需要保存默认文本值。

答案 4 :(得分:1)

这是一个想法:

如果我们可以生成keyborad事件,就像用户在textarea中输入一样,那么浏览器将自动处理撤消事件。因此,我们应该尝试为我们想要插入的文本模拟键盘事件,而不是仅仅添加/更改textarea的值。

根据MDN的文档(下面给出的链接),我们可以使用KeyboardEvent对象生成如下事件:

  var e1 = new KeyboardEvent(<type>, <details>);
  var b1 = <textbox>.dispatchEvent(e1);

其中:

  • <type>代表事件类型,例如keydownkeypresskeyup
  • <details>代表具有keycode
  • 等事件详情的对象
  • <textbox>表示我们想要触发事件的目标文本框

以下是JSFiddle,我尝试为给定字符串中的每个字符模拟keydownkeypresskeyup个事件。虽然它会触发相应的事件处理程序,但不会显示/添加到文本框中。

我注意到,当我使用我的代码模拟a的3个事件时,在文本框中键入a时生成的事件对象存在一些差异。差异是(在Firefox 50.1.0中测试时):

    当我模拟事件时,
  1. explicitOriginalTargetoriginalTarget不同;当我输入文本框时,它们都具有相同的值
  2. 当我输入文本框时,
  3. rangeParentrangeOffset值为null / 0;当我模拟事件时,他们有一些价值观
  4. 当我输入文本框时,
  5. isTrusted属性为true;当我模拟事件时false(对于使用脚本生成的任何事件,它将具有false
  6. MDN链接:

答案 5 :(得分:0)

即使这个问题是一年前的问题,我也想分享我的方式。

你可以这样做:

    $(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;

    }
  });

});

如果你想试验它http://jsfiddle.net/45Jwz/1/

,这是一个小提琴

我为了很好的理解而编写了这样的代码,但你当然应该更好地编写实际的代码而不是这个代码。

答案 6 :(得分:0)

具体的库和技术在很大程度上取决于您的堆栈。

我可以立即想到两种一般方式。

第一: 将以前的状态保存在控制器中。挂钩快捷方式并将内容替换为以前的状态。如果进行了其他修改,请删除钩子。或多或少你的2013年方法

这是一种快速的方法,如果你想要一个具有超过一次编辑历史的堆栈,那么它就不会很好。

第二: 观察textinput并定期将状态保存在堆栈中。钩入快捷方式。 (接管整个过程)。 这是一种更简洁的方式,因为您的修改在概念上与用户修改相同。

使用redux / flux架构可能非常简单 http://redux.js.org/docs/recipes/ImplementingUndoHistory.html

要捕获cmd / ctrl + z,您可以查看https://github.com/madrobby/keymaster

如果你详细说明你的堆栈/要求,我很乐意扩展这个答案。

答案 7 :(得分:0)

最简单的方法似乎是这样:

  • 在DOM textarea本身的属性中按钮点击保存以前的textarea内容。
  • 拦截BODY级别的键盘(你不知道何时以及如果按下Ctrl-Z,焦点将在何处)
  • (可选)还会根据需要拦截来自不同对象的多个事件:input,keyPress等,同样在BODY级别。

现在:

  • 当用户点击该按钮时,旧内容被保存并且自定义&#34;脏&#34; textarea的属性设置为true
  • 如果用户按Ctrl-Z或Cmd-Z,则撤消例程很简单(在jQuery中为$('#idOfTextarea').val($('#idOfTextarea').attr('prevContents'));。撤消例程也会清除&#34; dirty&#34;属性以便撤消没有被召唤两次。
  • (可选)如果用户改变另一个字段,点击其他地方等,textarea的dirty属性也被清除,并且更改变为可撤消。
  • (可选)具有自己的撤消功能的对象可以/必须停止事件传播,以避免其他撤消具有两种效果。

您可能想要或不想在textarea上拦截onChange。如果onChange触发并且未设置脏,则这是初始按钮单击,可能会被忽略。否则,它可能表示用户已将其自己的一些更改添加到单击的文本中。在这种情况下,您可能希望禁用撤消以保留这些更改,或者要求用户进行确认。

答案 8 :(得分:0)

你可以这样做,

<强> 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);
        }
     })
  })

您还可以查看并试用fiddle

答案 9 :(得分:0)

首先添加html,然后您可以使用按键事件撤消

你也可以在这里试试http://jsfiddle.net/surendra786/1v5jxaa0/                 

            <input type="text" class="reset actor_input" name="actor" value="add actors"></input>

            <input type="text" name="actors"></input>

            <div class="found_actors"></div>

            <div id="add" class="button_content">ADD</div>

            <div id="undo" class="button_content">UNDO</div>

            <div class="actors_list"><textarea readonly style="resize: none;" rows="20" cols="20" name="actors-list"></textarea></div>

        </div>

**然后添加jquery **

    var items = [];

$("#add").click(function() {
    // Push the new actor in the array
    items.push($("[name='actor']").val());
    populate();
});



$(document).keydown(function(e){
       if( e.which === 90 && e.ctrlKey ){
         console.log('control + z'); 
         if (items.length > 0) {
        // remove last element of the array
        items.splice(-1,1);
        populate();
    }
      }          
}); 

populate = function() {
    $("[name='actors-list']").text('');
    $("[name='actors-list']").append(items.join('&#13;&#10;'));
    $("[name='actors']").val(items.join(','));   
}

你可以在这里试试http://jsfiddle.net/surendra786/1v5jxaa0/

这对我有用

答案 10 :(得分:0)

这里有很多适用的答案可以满足您的需求。这是我在你的环境中会做的事情。这允许更改您最有可能使用的文本变量,您可以设置它或者用户可以使用其他字段设置它等。

codepen here

它的主旨就是这样。

$(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()
})

答案 11 :(得分:0)

这是一个简短的,非jQuery的解决方案,也可以在Firefox中使用(但没有撤消功能,例如当前接受的解决方案)

// 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>

此页面上的大多数解决方案已被弃用或模板代码过多。当前接受的解决方案可以执行类似的操作,但是代码更多。