今天遇到个问题,我的MocolaEditor之前有效的 Undo / Redo 功能突然失效了,这应该是编写HTML在线编辑器的PG都会遇到的一个问题。
先看看这个问题是什么:
<script>
function onchange(){
info.innerText=editor.innerText;
}
</script>
</HEAD>
<BODY>
<div style="width:500px;height:500px;border:1px solid #cccccc" contenteditable="true" ></div>
<div style="width:500px;height:50px;padding:4px;background-color:#F0f0f0"></div>
</BODY>
运行上面这段代码,你会发现,你的编辑器 将无法 Undo和Redo。
为什么会这样?
原因在于 info.innerText=editor.innerText; 这一脚本,改变了当前HTML渲染。起初我以为编辑器放在IFrame里就没事了,结果发现,放哪里都一样。
<script>
function onchange(){
info.innerText=Editor.document.body.innerText;
}
window.onload=function(){
Editor.document.designMode="ON";
Editor.document.onkeyup=onchange;
}
</script>
</HEAD>
<BODY>
<iframe style="width:500px;height:500px;border:1px solid #cccccc"></iframe>
<div style="width:500px;height:50px;padding:4px;background-color:#F0f0f0"></div>
</BODY>
Google的Writly (http://docs.google.com),有相同的问题。他的操作界面的弹出窗口都是用 div 来实现的。这样在显示和关闭的时候 必然会做一些Dom的操作,结果是只要弹出过窗口,Undo/Redo就失效了。
问题到此,应该很清楚了。那么如何解决呢?
既然浏览器的Undo/Redo不行了,那我就自己来写一个Undo/Redo方法。
每次文档发生改变的时候,我们可以把其HTMLCode 和 Selection 选区一起保存。存入一个数组里,只要数据都存好了,想undo/redo都是很轻松的事情了。
小Tips: 可以通过 document.selection.createRange().getBookmark()方来 来保存选区状态。
Undo的时候用 document.selection.createRange().moveToBookmark()方法来恢复选区。
以下是网上找的一段Undo/Redo 类。Typeing是为了对打字进行一些特殊处理,因为没有必要每输入一个字就增加一个UndoStep,这样太浪费内存空间了。 这里还需要对document.onkeydown函数做一些配合处理。
var CMSUndo=new Object(); CMSUndo.UndoLevels=new Array(); CMSUndo.CurrentStep=0; CMSUndo.MaxLevel=20; CMSUndo.Typing=false; CMSUndo.TypingCount=CMSUndo.TypingMaxCount=25; CMSUndo.SaveUndoStep=function(){ if(EMode == "Code") return; this.UndoLevels[this.CurrentStep]=this.GetSaveData(); this.CurrentStep++; this.UndoLevels=this.UndoLevels.slice(0,this.CurrentStep); if(this.UndoLevels>this.MaxLevel) this.UndoLevels.shift(); this.CurrentStep=this.UndoLevels.length; } CMSUndo.Redo=function(){ if(this.CurrentStep<this.UndoLevels.length-1) this.ApplyUndoLevel(this.UndoLevels[++this.CurrentStep]); } CMSUndo.Undo=function(){ if(this.UndoLevels.length<1) return; if(this.CurrentStep==this.UndoLevels.length) this.SaveUndoStep(); if(this.CurrentStep>0) this.ApplyUndoLevel(this.UndoLevels[--this.CurrentStep]); } CMSUndo.ApplyUndoLevel=function(cStep){ EditorDesignContent.body.innerHTML=cStep.htmlcode; if(cStep.selrange) { var range=EditorDesignContent.selection.createRange() range.moveToBookmark(cStep.selrange) range.select(); onEditContentSelChange(); } this.TypesCount=0; this.Typing=false; } CMSUndo.GetSaveData=function(){ var cStep=new Object(); cStep.htmlcode=EditorDesignContent.body.innerHTML; if (EditorDesignContent.selection.type=='Text') cStep.selrange=EditorDesignContent.selection.createRange().getBookmark(); return cStep; }
原文:http://www.mockte.com/rewrite.php/read-30.html