撤销和恢复功能,英文为:Undo和Redo,快捷键分别为Ctrl+Z和Ctrl+Y。它们是一组很常见的功能,在很多系统和软件中都有这两个功能。随着浏览器的功能越来越强,Web App将越来越占据重要位置,基于客户端JavaScript实现的撤销和恢复功能将越来越多,最近在工作中,完成了一个web系统的撤销和恢复功能,特写下一些心得以供分享。
首先介绍一下我们系统基本内容:我们系统是一套建站系统,通过不同模块的拖拽和设置便可以简单的制作网站。其中的设置操作是可以撤销恢复的,如果你不小心误操作,也可以很方便的返回未设置前的状态。而我开发的就是这个功能。下面我就介绍开发的一些内容吧。
1.不重新发明轮子,寻找合适的库帮忙
撤销和恢复功能是一个常用的功能,也是一个实现起来比较繁琐的功能。如果自己重头实现,要自己设计类结构,设计底层的栈数据结构,维护状态,很不方便也很容易出错。所以一般不同的客户端技术都有对应的封装。客户端JS也不例外,它就是undo.js,https://github.com/jzaefferer/undo
undo.js封装了一套比较通用的实现撤销和恢复功能架构,而它则帮你实现了内部的数据结构和状态的维护。使用undo.js主要有一下几步。
//初始化栈
var stack = new Undo.Stack();
//封装Command类
EditCommand = Undo.Command.extend({
constructor: function(textarea, oldValue, newValue) {
this.textarea = textarea;
this.oldValue = oldValue;
this.newValue = newValue;
},
execute: function() {
},
undo: function() {
this.textarea.html(this.oldValue);
},
redo: function() {
this.textarea.html(this.newValue);
}
});
//操作入栈
stack.execute(new EditCommand(text, startValue, newValue));
//undo
stack.undo();
//redo
stack.redo();
2.轮子不利索,缝缝补补换新颜
使用undo.js确实对于开发撤销和恢复功能提供了方便,原本可以结束了,那我也不写这篇文章了。不过在使用了undo.js几次后,我慢慢觉得代码有点别扭,于是我结合之前的开发重新回过头研究一下撤销和恢复的开发。在经过了一些研究后我发现其中Undo.Command的封装上过于简单了。虽然简单就是美,但是却导致了开发时一些代码大量的重复,介于此,我又在其上做了一层封装。
首先说说Undo.Command的封装的一些问题:
1.constructor、execute基本上是鸡肋,写法都一样。
2.撤销和恢复要做的操作分为两个部分:逻辑和数据,很多操作undo、redo的逻辑是一样的,只不过是数据不同。比如:移动模块位置操作。
3.缺少对于数据相关的函数,比如:setOldVal(val),setNewVal(val)
4.缺少自动判断是否修改的过程,举个例子:以通过键盘移动为例,当模块在最右边界,实际并没有移动,就不需要入栈。
5.缺少对于入栈的封装,比如:直接使用insert就可以,如果加一些insertWithNewVal(newval)、insertWithVals(oldval,newval)就更好了。
于是我经过我的封装后,开发撤销恢复这可以这样写:
EditCommand =Undo.Command.createGlobalCommand(function(val){
val.textarea.html(val.content);
},{returntype:'class'});
//入栈
new EditCommand().insertWithVals({textarea:text,content:oldvalue},{textarea:text,content:newvalue});
//你也可以这样直接入栈
Undo.Command.createGlobalCommand(function(val){
val.textarea.html(val.content);
}).insertWithVals({textarea:text,content:oldvalue},{textarea:text,content:newvalue});
3. 高级应用,加入事务,多个Command合为一
对于一些复杂的应用,一个大的操作需要由多个小操作组合而成,如果一个Undo Command也可以组合,那就完美了。在数据库中,有一个机制叫事务,可以让多步操作转为一个原子操作。我模仿了它也在undo.js中加入这个功能,保证了在事务中通过insert入栈的Command最终会统一成一个撤销恢复操作。示例代码如下:
Undo.Transaction.start();
//在这之间insert的Command会被转为一步撤销恢复操作
Undo.Transaction.end();
4. 最高境界,彻底隐藏Undo Command (未完成)
这部分每个系统都有不同做法,我提一下我的一些想法。
在KindEditor等富文本编辑器中,它们也实现了撤销恢复操作,以KindEditor为例,它的API非常简单,addBookMark(),只要调用一下即可,为什么和我们前面看到的差那么多呢?关键在于我上面提到的两个东西:逻辑和数据。 对于KindEditor而言,其操作的这两个方面都是统一的,数据就是编辑器的html代码,逻辑就是把对应html代码塞入编辑器中。由此我们得出一个结论,只要逻辑和数据统一,我们就可以隐藏Undo Command。
可是这个好像只对特殊的应用有效啊,不具备通用性啊,我不同操作的逻辑和数据都是不同的,如何统一?可以,我提供一个思路:关于数据的统一,我们可以构造一个数据容器,在这里统一管理数据,当容器中某数据更改了,就会自动生成一个Command。那逻辑呢,怎么统一,这个可以使用类似event的分发机制,统一函数入口,只要保证数据和逻辑的一致性就可以了。
如果遇上了一个操作需要多个数据变化怎么办?这时可以使用事务机制(现在知道它的用途了吧)
问题是怎么找到合适的切入点,这个我也没有好的办法,大家根据自己应用的特点加以寻找,基本上所有应用都有这样的切入点。
以我公司的建站系统为例:
我把每个模块加入一个容器管理数据,当一个操作改变模块,会想容器加入新的数据,比如:拖动位置后会修改pos字段,便会自动生成一个undo Command。当使用undo调用这个Command的函数时,它会向这个模块发送一个类似event中trigger的回调,并将对应的参数比如:数据key、value,undotype(是undo还是redo操作),由模块回调负责具体模块。这样就隐藏了Undo Command的底层实现,只专注逻辑了。
最后,我把改进的undo.js发布在了iteye的代码收藏里了,其中的代码很粗糙,如果有兴趣就将就着看一下,你可以通过如下网址查看: http://renzhen.iteye.com/code 。
作为一个决心成为优秀开发人员的我,竟然我怎么会画图,只能不停的打字,决定好好学学画流程图,一图胜万语啊。还有iteye的富文本编辑器排版有点烂,不好使,搞了我好久。
分享到:
相关推荐
通过这个控件,C#客户端能够执行JavaScript代码,实现一些在Web浏览器中常见的功能,比如颜色操作、计算或动态效果。 颜色计算器可能利用JavaScript的内置函数处理颜色的RGB、HEX或HSL表示,并提供用户友好的界面来...
总的来说,"为每个人生成客户端JavaScript PDF.zip"的资源是学习和实践在客户端生成PDF文件的宝贵资料,无论你是Web开发者,还是希望为用户提供离线打印或下载功能,jsPDF都是一个强大的工具。通过深入理解和实践,...
【标题】:利用客户端JavaScript脚本制作自测型教学网页 【描述】:本文主要讨论如何利用客户端JavaScript脚本创建自测型教学网页,包括网页试卷的制作、JavaScript脚本的嵌入以及设置指向本地计算器的超链接。 ...
总结来说,jQuery简化了JavaScript的许多操作,让开发者能够更快速地实现复杂的交互效果。而纯JavaScript则更底层,更灵活,虽然代码量可能稍大,但理解了其工作原理,对提升JavaScript技能大有裨益。无论选择哪种...
在本项目"JavaScript实战项目之飞机大战"中,我们将探讨如何使用JavaScript这门客户端脚本语言来构建一个互动的游戏。JavaScript通常用于网页交互,而在这个案例中,它被用来实现了一个经典的游戏——飞机大战。通过...
总结:通过以上分析,我们可以看出客户端JavaScript在现代互联网开发中占据着核心地位。无论是基础的网络协议还是高级的安全技术,都与客户端JavaScript紧密相关。掌握这些知识点将有助于开发者更好地理解和应用...
JavaScript客户端输入验证是Web开发中不可或缺的一环,它主要用于在用户提交数据到服务器之前检查数据的有效性和格式。这种验证可以防止无效数据的提交,减轻服务器的负担,提高用户体验,因为错误提示可以即时显示...
总的来说,JavaScript富客户端编程涵盖了广泛的技能和概念,从基础的DOM操作到复杂的异步通信和状态管理,开发者需要持续学习和实践,才能驾驭这个强大的工具,创造出功能丰富、用户体验优秀的Web应用。
总结起来,Android的WebView提供了丰富的功能,让客户端与JavaScript的交互变得简单。通过设置JavaScript接口,我们可以实现Java方法的调用和数据的传递,极大地丰富了Android应用的功能。同时,利用...
"JavaScript实战"这本书深入探讨了在实际开发中应用JavaScript的各种技术和策略。以下是对标题、描述和标签所涵盖的知识点的详细说明: 1. **JavaScript简史**:JavaScript最初由Brendan Eich在1995年为Netscape ...
本套源码提供了一个C#实现的JSON生成器,能够将数据结构转换为JSON格式,便于传输到客户端的JavaScript后台。 首先,我们要理解JSON的基本结构。JSON由键值对组成,键是字符串,值可以是各种数据类型,包括字符串、...
JavaScript是一种广泛应用于网页和网络应用的脚本语言,它在客户端运行,为用户提供动态交互体验。在现代Web开发中,JavaScript不仅可以操作DOM、处理事件,还可以实现与服务器的异步通信(Ajax)以及读取客户端的...
JavaScript是Web开发中不可或缺的一部分,尤其在客户端验证和页面特效制作方面发挥着重要作用。这个"javascript客户端验证和页面特效制作项目案例"旨在帮助学习者掌握JavaScript的核心概念,并将其应用于实际项目中...
javascript获取客户端网卡MAC地址和IP地址和计算机名
如果不打算深究JavaScript幕后机制的话,运用本篇的知识便能胜任一般的JavaScript开发了。JavaScript高级篇:揭示JavaScript的运行机理和高级应用,如面向对象编程、函数式编程和元编程。Ajax篇:Ajax是上帝赐予...
JavaScript是一种广泛应用于...通过学习和实践这些基本概念,你将能够编写出交互性强、功能丰富的客户端JavaScript代码。记住,不断练习和探索新的API及框架(如jQuery、React、Vue等)是提升JavaScript技能的关键。
JavaScript是客户端脚本语言,让网页具有动态性和用户交互性。它可以直接操作DOM(Document Object Model)来改变HTML内容,使用AJAX(Asynchronous JavaScript and XML)实现异步数据交换,更新页面无需刷新。...
3. 客户端 JavaScript:了解客户端 JavaScript 的起源和背景;掌握客户端 JavaScript 的工作方式;掌握客户端 JavaScript 的应用。 六、项目(或学习情境)设计 1. 用户注册验证:掌握常用网页登录技巧,用户名...
以下是对客户端脚本验证的详细总结: 一、验证目的 客户端脚本验证的主要目标是: 1. 提高用户体验:通过即时反馈,用户可以在提交表单前发现错误,无需等待服务器响应。 2. 数据保护:防止无效或恶意数据提交到...