这两天在开发某商学院的网站,里面有涉及到课程的模块,客户希望在网站的首页显示一个日历,在有课程的日期加上显眼的标识,使网站用户一眼看到日历后就能知道哪天商学院有课程以便他们安排时间报名修读。
这个功能挺有意思的,符合本人及非常小器公司一直所坚持的“为客户创造价值”的理念,它真正从用户的角度思考问题了,客户真正需要的、实用的东西是我最喜欢开发的产品因为我觉得不能为客户创造价值的产品最终必定不能为自己创造价值,这个世界是讲因果的。
刚开始的时候感觉用第三方的东西挺麻烦的,一来要研究别人的接口,规范等一大堆的东西,有什么地方满足不了要求的时候就更加麻烦了,要去读懂别人的源码再修改,还不如自己全新开发来得直接简单,但日历这东西,说复杂不复杂,但要做得好也有一定工作量,利用google快速了解了目前几个比较知名的轻量级日历插件的接口、提供的配置项及功能情况后,决定不重复制造轮子,在jquery ui的datepicker控件上进行开发,因为它虽然功能简单,但提供的配置项比较灵活,不需要经过大调整就能实现想要的效果,而且自备多种UI风格,相信总有一款能很好地与网站风格相融合,呵呵。废话不多说了,下面来看看实现的过程吧,希望能被有需要的朋友借鉴,同时欢迎各位大神提出宝贵建议。首先看看效果:图中2013年4月30号被一个红色的圈圈住了,这是一个设计时的一个约定,日期被圈住说明当天是有课程的,点击这个日期的时候再列出这天有哪些课程。
,UI的风格其实就是jquery ui中的蓝色主题版本的,由于本身网站是以蓝色作为基调的,所以用蓝色主题的UI能与网站整体融合得非常好,可能很多人印象中都觉得datepicker是用来选日期的,以前应用它的时候都是有一个输入框,点击输入框后才弹出这个日历面板,选择一个日期后触发回调,把选中的日期更新回到一个特定的元素当中,日期选择控件的使命就完成了,但这里,它做的却是完全不同的事情。它就像台历一样,只负责显示日期列表及标记一些特定日子的作用,当然,它比台历更加复杂和先进一点,因为它是根据后台的课程开课日期设置来自动在日历中做标记的。
实现的细节:
1)怎样让datepicker默认就显示在指定的地方而不是通过输入框焦点触发? 这点其实很简单,通过查datepicker的api就可以知道datepicker初始化的时候会自动判断调用它的元素类型是什么,如果是input,它就会等待点击触发,如果是div,它默认就会显示出来了,所以,这里,只需要在要显示的位置放一个div,然后用jquery的选择器找到这个元素,并且在上面调用datepicker()方法就可以了,示例:$('.datePickerWraper').datepicker();
2)怎样让datepicker符合设计的要求?这个其实也简单,用CSS配合一下了,我的建议是不要直接在jquery ui的样式上面改,一来影响它自身的完整及独立性,到时或许会用到它的控件,如果直接改会导致一些意想不到的情况发生,我认为比较好的办法是在特定的页面下用自己的样式把默认的样式覆盖掉以使控件的尺寸符合我们的预期,即方便又灵活。
3)怎样在特定的日期加上特殊的标记?这个是课程日历的关键所在。首先,当然是需要课程开课日期的数据了,由服务端提供的课程信息数组而来,这里就不再赘述了,研究了datepicker的api,发现它提供了一个beforeShowDay的钩子,所有的日期在渲染之前都会通过这里的,有这个机制就好办了,在这个钩子里添加代码,遍历课程列表,如果当前单元格的日期与课程的开课日期是同一天,就返回一个带有三个元素的数组变量,分别代表日期是否可选,要在日期容器里额外添加的class属性及单元格的hover事件触发时显示的内容,相当于a的title。由于每次渲染日期时都会经过这个方法处理,所以,只要把课程列表初始化好,在用户切换月份和年份的时候都会自动处理,不需要再在切换年月份的时候做干预,非常简便就能达到想要的效果了。
4)怎样实现没有课程的日期不可点击(选择),有课程的日期点击(选择)后显示这天的课程列表?第三点中提到,beforeShowDay接收的返回参数中,第一个参数就是是否可以选择的标记,所以,只有在比较到有开课的日期才返回true,否则返回false就能达到控制日期是否可选的效果了,但是需要注意的一点是,默认的样式中,不可选的日期的opacity(不透明度)是1来的,也就是,基本上处于蒙住的状态了,看起来很不和谐,所以我通过CSS把它的默认样式修改了,而在返回false的日期中,jquery ui自动是把它的日期文本由a标签改成 span标签括住的,所以不用担心点击锚点会引起错误的问题。选中有课程的日期时,会触发控件的onSelect事件,弹出课程列表的操作写在onSelect事件的响应方法里面就可以了。下面是初始化控件的完整代码,仅供参考。
var curYear = new Date().getFullYear();
$('.calendar').datepicker({
yearRange: curYear+':'+curYear,
prevText: '前一月',
nextText: '后一月',
yearSuffix: '年',
dateFormat: "yy-mm-dd",
showMonthAfterYear: true,
dayNamesMin: ['日','一','二','三','四','五','六'],
monthNames: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
onSelect: function(text, instance){
var arr = text.split('-');
var year = arr[0], month = arr[1], day = arr[2];
var curDateTime = new Date(year, month-1, day, 8, 0, 0);
var dialogTitle = '商学院于'+text+'的课程安排';
var curDateCourseList = [];
var dialogHeight = 40;
for(var i in courseList){
var time = courseList[i].begin_date * 1000;
if(time == curDateTime.getTime()){
curDateCourseList.push("<a title='点击查看详情' href='/course/index.php?id="+courseList[i].id+"' target='_blank'>"+courseList[i].name+"</a> <span style='color:#888;'>地点:"+ courseList[i].address +"</span><br>");
dialogHeight += 35;
}
}
$(".dateCourseList").html(curDateCourseList.join(',')).dialog({title:dialogTitle, height:dialogHeight});
$(".dateCourseList").dialog("open");
return false;
},
beforeShowDay: function(date){
for(var i in courseList){
var courseTime = (courseList[i].begin_date - 60 * 60 * 8)*1000;
if(courseTime == date.getTime()){
return [true, 'hasCourse', '课程日'];
break;
}
}
return [false, 'noCourse', '没有课程'];
}
});
demo就不设独立的网址了,有兴趣看看效果的朋友可以直接访问http://center.royalfield.org看课程日历就清楚了。