仅使用纯JavaScript的textarea撤消和重做按钮

时间:2019-04-18 01:16:03

标签: javascript textarea

我知道Chrome内置了CTRL Z和Y的撤消和重做功能。但是使用纯JavaScript,“撤消/重做”按钮是否可以在 textarea 上工作?

我发现这里的代码(由Neil回答)很有用,但需要jQuery: How do I make undo/redo in <textarea> to react on 1 word at a time, word by word or character by character?

1 个答案:

答案 0 :(得分:0)

这是我制造的StateMaker。它用于撤消,重做和保存。这很简单。很好。

问题是e.ctrlKey && e.key === 'z'等在Firefox中e.preventDefault()时有奇怪的行为,因此我删除了该部分。下面的代码被认为是不完善的,因为如果单词和状态的长度相同,它会覆盖最后一个状态。但是,如果我不这样做,它会保存每个字符的状态,这也是可行的。另一个解决方案是根据时间保存状态。在此示例中,我没有走那条路线。

//<![CDATA[
/* external.js */
/* StateMaker created by Jason Raymond Buckley */
var doc, bod, I, StateMaker; // for use on other loads
addEventListener('load', function(){
doc = document; bod = doc.body;
I = function(id){
  return doc.getElementById(id);
}
StateMaker = function(initialState){
  var o = initialState;
  if(o){
    this.initialState = o; this.states = [o];
  }
  else{
    this.states = [];
  }
  this.savedStates = []; this.canUndo = this.canRedo = false; this.undoneStates = [];
  this.addState = function(state){
    this.states.push(state); this.undoneStates = []; this.canUndo = true; this.canRedo = false;
    return this;
  }
  this.undo = function(){
    var sl = this.states.length;
    if(this.initialState){
      if(sl > 1){
        this.undoneStates.push(this.states.pop()); this.canRedo = true;
        if(this.states.length < 2){
          this.canUndo = false;
        }
      }
      else{
        this.canUndo = false;
      }
    }
    else if(sl > 0){
      this.undoneStates.push(this.states.pop()); this.canRedo = true;
    }
    else{
      this.canUndo = false;
    }
    return this;
  }
  this.redo = function(){
    if(this.undoneStates.length > 0){
      this.states.push(this.undoneStates.pop()); this.canUndo = true;
      if(this.undoneStates.length < 1){
        this.canRedo = false;
      }
    }
    else{
      this.canRedo = false;
    }
    return this;
  }
  this.save = function(){
    this.savedStates = this.states.slice();
    return this;
  }
  this.isSavedState = function(){ // test to see if current state in use is a saved state
    if(JSON.stringify(this.states) !== JSON.stringify(this.savedStates)){
  return false;
}
return true;
  }
}
var text = I('text'), val, wordCount = 0, words = 0, stateMaker = new StateMaker, save = I('save');
text.onkeyup = function(e){
  save.className = undefined; val = this.value.trim(); wordCount = val.split(/\s+/).length;
  if(wordCount === words && stateMaker.states.length){
    stateMaker.states[stateMaker.states.length-1] = val;
  }
  else{
    stateMaker.addState(val); words = wordCount;
  }
}
I('undo').onclick = function(){
  stateMaker.undo(); val = text.value = (stateMaker.states[stateMaker.states.length-1] || '').trim();
  text.focus();
  save.className = stateMaker.isSavedState() ? 'saved' : undefined;
}
I('redo').onclick = function(){
  stateMaker.redo(); val = text.value = (stateMaker.states[stateMaker.states.length-1] || '').trim();
  text.focus();
  save.className = stateMaker.isSavedState() ? 'saved' : undefined;
}
save.onclick = function(){
  stateMaker.save(); text.focus(); this.className = 'saved';
}
}); // end load
//]]>
/* external.css */
*{
  padding:0; margin:0; border:0; box-sizing:border-box;
}
html,body{
  width:100%; height:100%; background:#aaa; color:#000;
}
input{
  font:22px Tahoma, Geneva, sans-serif; padding:3px;
}
#text{
  width:calc(100% - 20px); height:calc(100% - 70px); font:22px Tahoma, Geneva, sans-serif; padding:3px 5px; margin:10px;
}
#undoRedoSave{
  text-align:right;
}
input[type=button]{
  padding:0 7px; border-radius:5px; margin-right:10px; border:2px solid #ccc;
}
input[type=button].saved{
  border:2px solid #700;
}
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
  <head>
    <meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1' />
    <title>Test Template</title>
    <link type='text/css' rel='stylesheet' href='external.css' />
    <script type='text/javascript' src='external.js'></script>
  </head>
<body>
  <textarea id='text'></textarea>
  <div id='undoRedoSave'>
    <input id='undo' type='button' value='undo' />
    <input id='redo' type='button' value='redo' />
    <input id='save' type='button' value='save' />
  </div>
</body>
</html>