蛙蛙推荐:用javascript作一个通用向导

摘要:向导可以让你的网站用户快速上手使用你的web应用,提高网站的吸引力。向导一般分为好几个步骤,每个步骤收集一些数据,并且支持退回功能,所有步骤完成后可以得到每一步的收集结果。这里给大家展示一种比较通用,灵活且简单的向导框架。

1、界面设计

index.html:只提供了一个向导显示位置的占位符

<html>
<head>
<title>礼物推荐向导</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js" type="text/javascript"></script>
<script src="wizard.js" type="text/javascript"></script>
</head>
<body>
<div id="wizard"></div>
</body>
</html>

style.css:默认情况下向导里有一个h2呈现的标题,一个ul呈现的主要内容,一个div呈现的按钮条,我们简单设计了一下他们的默认外观,实际应用中大家可以自由的美化它们。

body{
margin
:0;
}
/*向导容器*/
#wizard
{
height
:400px;
width
:600px;
background-color
:#999;
}
/*向导的主体内容,用列表展示*/
#wizard ul
{
margin
:10px;
height
:80%;
}
/*横向显示列表内容*/
#wizard li
{
display
:inline-block;
margin
:10px;
cursor
:pointer;
}
/*列表的标题*/
#wizard h2
{
margin
:10px;
}
/*列表的功能条,如返回按钮*/
#wizard .bar
{
margin
:10px;
clear
:both;
}

2、准备每一步骤

向导可以分为每一步骤,每个步骤需要呈现内容,捕捉用户选择,提供标题等功能,我们让每一步都自己负责自己的事情,但要符合我们规定的一些契约。

每一个步骤用一个函数表示,第一个参数data_key是选择本步骤数据的关键字,一般用于上一个步骤的结果决定下一个步骤显示数据的情况,第二个参数result_callback是个回调函数,就是在本步骤获取结果时调用,它用于和向导类进行通信,向导类在得到上一步的结果后存储结果并跳向到下一步。

该函数返回一个二元组,第一个元素是本步骤的标题,第二个元素是本步骤主体部分的UI。

我们的示例是一个礼物推荐系统,共分三步,第一步选择送礼对象,第二步选择关键字,其中第一步的选择结果会影响到第二步显示,第三步选择价格区间,如下就是代码的实现,其中绘制界面和事件捕捉用了jquery来简化操作。

function step1(data_key, result_callback){
var targets = ['女朋友','男朋友','父亲','妈妈','孩子'];
var warpper = $('<ul></ul>')
$.each(targets,
function(k,v){
$(
'<li>'+v+'</li>').click(function(){result_callback(v)}).appendTo(warpper);
});
return ['第一步:请选择送礼物的对象',warpper];
}
function step2(data_key, result_callback){
var tags = {
'女朋友':['创意','可爱','浪漫','激情','实用','数码',
'自制','毛绒玩具','衣服','包包'],
'男朋友':['男士用品','温馨','实用','数码','创意','衣物'],
'父亲' :['男士用品','健康','植物','衣物'],
'妈妈' :['温馨','健康','创意','护肤品','实用'],
'孩子' :['玩具','学习用品','实用','数码']
};
var warpper = $('<ul></ul>')
$.each(tags[data_key],
function(k,v){
$(
'<li>'+v+'</li>').click(function(){result_callback(v)}).appendTo(warpper);
});
return ['第二步:请选择关键词',warpper];
}
function step3(data_key, result_callback){
var price_level = ['便宜','普通','稍贵','贵重'];
var warpper = $('<ul></ul>')
$.each(price_level,
function(k,v){
$(
'<li>'+v+'</li>').click(function(){result_callback(v)}).appendTo(warpper);
});
return ['第三步:请选择价格区间',warpper];
}

3、向导类的实现

向导类要设置向导所在的DOM元素,要执行的步骤列表,向导完成后执行的回调,向导还应该提供上一步和下一步的方法,所以我们用一个类来表示向导,在构造函数里传入DOM容器,步骤列表和回调函数,用prototype给类增加三个方法。render用来呈现某一步骤的UI,并在本步骤收集结果的回调里推向下一步,如果本步骤是最后一步,则调用向导执行完成的回调函数。

另外两个next和back函数分别是执行上一个步骤和下一个步骤,这两个函数实用index的私有变量来维持整个向导的状态

function Wizard(container, steps, callback){
this.container = container; //向导容器
this.steps = steps; //向导步骤
this.callback = callback; //向导执行完毕执行的回调
this.collect_data = []; //保存向导每一步骤的结果
this.index = -1; //当前执行在那一步骤
}
//绘制某一步骤
Wizard.prototype.render = function(step, this_result){
var me = this;
//执行该步骤并得到该步骤的UI
var to_append = step(this_result,function(result){
me.collect_data.push(result);
//收集本步骤结果
//向导执行完毕时调用回调函数,否则执行下一步
if(me.collect_data.length == me.steps.length)
me.callback(me.collect_data);
else
me.next(result);
});
//绘制本步骤的UI
this.container.empty();
this.container.append("<h2>"+to_append[0]+"</h2>");
this.container.append(to_append[1]);
if(this.index > 0){
//后退按钮
this.container.append($("<div class='bar'><a href='javascript:;'>后退</a></div>")
.click(
function(){me.back()}
));
}
}
//执行下一步
Wizard.prototype.next = function(this_result){
if(this.index >= this.steps.length -1)
return;
var step = this.steps[++this.index];
this.render(step,this_result);
}
//后退到上一步
Wizard.prototype.back = function(){
if(this.index <= 0)
return;
var step = this.steps[--this.index];
//步骤回到上一步,但上一步的数据需要上上一步的结果来决定
this.collect_data = this.collect_data.slice(0, this.index);
this.render(step, this.collect_data[this.index - 1]);
}

4、小结

本向导结构简单,可定制性强,结合了javascript的函数式编程特性和面向对象的特性,体现了javascript的强大和便利。

其中wizard类里界面绘制的部分和步骤函数里界面绘制的部分还是存在一些耦合,继续重构的话,可以把所有绘制界面的部分再抽象到一起,使界面改动更方便。

posted @ 2011-07-09 12:48 蛙蛙王子 Views(1846) Comments(14) Edit 收藏

 回复 引用 查看   
#1楼2011-07-09 13:03 | 首席技术官      
就不能贴一下演示地址吗。。。
 回复 引用 查看   
#2楼2011-07-09 13:03 | xiaohuatu      
沙发 好文要顶 必须的
 回复 引用 查看   
#3楼2011-07-09 13:56 | aisoon99      
引用首席技术官:就不能贴一下演示地址吗。。。

你为什么不自己实践一下?

 回复 引用 查看   
#4楼2011-07-09 15:18 | wilensky      
感谢分享,有演示就更完美了
 回复 引用 查看   
#5楼2011-07-09 16:42 | YoyiorLee      
引用wilensky:感谢分享,有演示就更完美了

就是,这个也叫用户体验!写这种技术文章最还能有个演示的demo,直接查看结果

借你宝地,打个广告啊,请谅解一下,看在我一贯支持你的份上,别删除哦。

金融行业招聘.NET中高级开发人员好工作向朋友门分享[工作地点北京知春路地铁站附近]
年薪5-12万,招聘C#.NET
http://www.cnblogs.com/jirigala/archive/2011/07/09/2101920.html
欢迎大家转发一下

 回复 引用 查看   
#7楼2011-07-10 17:15 | NewSea.      
得说你一句。

楼主要有更开阔的心胸,才佩道貌岸然的说教。

 回复 引用 查看   
#8楼[楼主]2011-07-11 13:21 | 蛙蛙王子      
@NewSea.
啥意思,哥?

 回复 引用 查看   
#9楼2011-07-12 08:36 | 首席技术官      
引用aisoon99:
引用首席技术官:就不能贴一下演示地址吗。。。

你为什么不自己实践一下?

别用这样的语气跟我讲话,要知道我是在提醒你,不是每个人都愿意去实践你那段臭代码的。

 回复 引用 查看   
#10楼2011-07-12 13:31 | aisoon99      
引用首席技术官:
引用aisoon99:
引用首席技术官:就不能贴一下演示地址吗。。。

你为什么不自己实践一下?

别用这样的语气跟我讲话,要知道我是在提醒你,不是每个人都愿意去实践你那段臭代码的。

不好意思,那代码不是我写的,你是心虚才觉得我的说话语气不好吧,什么学习态度啊,还臭代码,有病吧你,你技术很强吗,一般你这样能装的人技术都不咋地

 回复 引用 查看   
#11楼[楼主]2011-07-13 09:53 | 蛙蛙王子      
@首席技术官
当时比较着急,没有做好演示和贴图,因为要设计html,css和js,在博客上我不知道怎么做演示。

 回复 引用 查看   
#12楼2011-07-13 09:59 | 首席技术官      
引用蛙蛙王子:
@首席技术官
当时比较着急,没有做好演示和贴图,因为要设计html,css和js,在博客上我不知道怎么做演示。

我当时就像表达一种意思:用户体验。“结果”是绝大多部分人最愿意关注的,因为结果很直观,相当于你把你做的东西的功能向别人展示了一遍,对别人有没有价值很快就可以体现出来了。或许这也是现在社会的一种浮躁氛围吧,包括我自己。

 回复 引用 查看   
#13楼[楼主]2011-07-13 10:02 | 蛙蛙王子      
@首席技术官
恩,谢谢,我会努力的,有时间把demo加上

 回复 引用 查看   
#14楼2011-07-14 22:23 | 卖身葬小强      
感谢楼主的分享,俺菜鸟,拷贝下来不懂怎么用,也看不到什么效果,
能上传个demo么?谢谢
or mailto: wuyuanhui@qq.com,thanks~