25

我有一个文本区域和一个按钮。单击该按钮会导致将文本插入到文本区域中。

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

4

11 回答 11

12

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

为此,您需要为不同的浏览器使用不同的代码。幸运的是,在所有主流浏览器中,只有 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>

测试:

  • 法郎:50
  • 铬:55
  • 边缘 38
  • 野生动物园:5.1
  • 歌剧:42
于 2017-01-19T02:33:47.743 回答
7

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

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

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

document.getElementById("your-textarea").dispatchEvent(textEvent);
于 2012-11-28T02:58:35.867 回答
3

将原始值保存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');
});
于 2012-11-28T02:55:34.317 回答
0
$("#target").keypress(function(event) {
  if ( event.which == 'Z' && first press == 'Cmd' && second press == 'Ctrl') {//check for key press
     event.preventDefault();
    text.value = defaultValueForTextField
   }
});

这应该是你正在寻找的。第一次和第二次按需要保存,因为您需要组合印刷机。不过,您确实需要保存默认文本值。

于 2012-11-28T03:01:28.190 回答
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/

我写这样的代码是为了更好地理解,但你当然应该比这更好地写出实际的代码,而且不那么冗长。

于 2014-05-04T12:06:58.860 回答
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

于 2017-01-18T14:16:44.277 回答
0

先添加html,然后可以使用keypress事件撤消

你也可以在这里试试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/

这对我有用

于 2017-01-18T16:47:41.243 回答
0

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

我可以立即想到两种通用方法。

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

这是一种快速的方法,如果您想要一个具有多次编辑历史的堆栈,则效果不佳。

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

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

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

如果您详细说明您的堆栈/要求,我将很乐意扩展此答案。

于 2017-01-16T12:52:42.253 回答
0

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

在这里写代码

它的要点是这样的。

$(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()
})
于 2017-01-19T00:05:03.903 回答
0

这是一个想法:

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

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

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

在哪里:

  • <type>表示事件类型,例如keydown,keypresskeyup
  • <details>表示具有事件详细信息的对象,key例如code
  • <textbox>表示我们要在其上触发事件的目标文本框

这是一个JSFiddle,我尝试在其中模拟给定字符串中每个字符的keydown,keypresskeyup事件。尽管它触发了适当的事件处理程序,但字符并没有显示/添加到文本框中。

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

  1. explicitOriginalTargetoriginalTarget与我模拟事件时不同;当我在文本框中输入时两者都具有相同的值
  2. rangeParent当我在文本框中输入时,rangeOffset值是null/ ;0当我模拟事件时,它们有一些值
  3. isTrusted属性是true我在文本框中输入时;当我模拟它的事件时false(对于使用脚本生成的任何事件,它都会有false

MDN 链接:

于 2017-01-18T03:55:56.577 回答
0

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

  • 在 DOM textarea 本身的属性中保存按钮单击时以前的 textarea 内容。
  • 在 BODY 级别拦截键盘(您不知道何时以及是否按下 Ctrl-Z 时焦点在哪里)
  • (可选)还可以根据需要拦截来自不同对象的尽可能多的事件:输入、keyPress 等,也在 BODY 级别。

现在:

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

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

于 2017-01-18T12:43:20.853 回答