我知道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?
答案 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>