前言

最近开始整理我们的单页应用框架了,虽然可能比不上MVVM模式的开发效率,也可能没有Backbone框架模块清晰,但是好歹也是自己开发出来

而且也用于了这么多频道的东西,如果没有总结,没有整理,没有开源就太可惜了......所以最近开始整理框架相关的东西,争取抽象一点东西出来

框架出来还需要一点时间,但是框架会需要相关的UI库,这个东西可以先有思路,最后再根据框架做一点调整吧

日历对于UI插件而言还是比较难的,里面涉及到的东西很多,就阴历与阳历一块就有很多东西,然后涉及到很多算法,其中节日的设置更是有一定动态性

各种各样的需求也是莫名其妙,所以我们今天便来实现一个简单的日历插件吧,当然他的主要应用场景还是单页应用

构思

首先,我们这里用这套东西实现继承

  1. var arr = [];
  2. var slice = arr.slice;
  3.  
  4. function create() {
  5. if (arguments.length == 0 || arguments.length > 2) throw '参数错误';
  6.  
  7. var parent = null;
  8. //将参数转换为数组
  9. var properties = slice.call(arguments);
  10.  
  11. //如果第一个参数为类(function),那么就将之取出
  12. if (typeof properties[0] === 'function')
  13. parent = properties.shift();
  14. properties = properties[0];
  15.  
  16. function klass() {
  17. this.initialize.apply(this, arguments);
  18. }
  19.  
  20. klass.superclass = parent;
  21. klass.subclasses = [];
  22.  
  23. if (parent) {
  24. var subclass = function () { };
  25. subclass.prototype = parent.prototype;
  26. klass.prototype = new subclass;
  27. parent.subclasses.push(klass);
  28. }
  29.  
  30. var ancestor = klass.superclass && klass.superclass.prototype;
  31. for (var k in properties) {
  32. var value = properties[k];
  33.  
  34. //满足条件就重写
  35. if (ancestor && typeof value == 'function') {
  36. var argslist = /^\s*function\s*\(([^\(\)]*?)\)\s*?\{/i.exec(value.toString())[1].replace(/\s/i, '').split(',');
  37. //只有在第一个参数为$super情况下才需要处理(是否具有重复方法需要用户自己决定)
  38. if (argslist[0] === '$super' && ancestor[k]) {
  39. value = (function (methodName, fn) {
  40. return function () {
  41. var scope = this;
  42. var args = [function () {
  43. return ancestor[methodName].apply(scope, arguments);
  44. } ];
  45. return fn.apply(this, args.concat(slice.call(arguments)));
  46. };
  47. })(k, value);
  48. }
  49. }
  50.  
  51. klass.prototype[k] = value;
  52. }
  53.  
  54. if (!klass.prototype.initialize)
  55. klass.prototype.initialize = function () { };
  56.  
  57. klass.prototype.constructor = klass;
  58.  
  59. return klass;
  60. }

其次,我们的日历做出来应该是可定制化的,可定制化的粒度控制到每一个单元格,意思是每一个单元格是可操作的

这个时候最好的解决办法就是模板,并且释放一个操作某个日期的接口,比如我们现在要实现阴历节日或者阳历节日完全是实现抽象的日历,这样可以最大的提高扩展性

所以,我们这里的第一步是实现一个最基本的抽象日历

abstract.calendar

像日历这类插件,我首先还是想到用表格来做,但是CSS3的出现也能让我们的代码很好的实现,所以我这里使用li做,具体实现我们后面再说,我们要完成的第一个事情是

渲染当月

我们做的第一个事情是给一个日期,然后当月的数据便出来了,比如我们这里给的是20140420,就是当前日期,然后便需要形成这个月的日期,这里就涉及到一连串东西了

解决这个问题,我们需要第一个api,算出给定日期一共有多少天,第二步便是排列第一个日期为星期几即可

众所周知,计算月份天数时候有一个例外的情况便是闰年的二月,所以我们需要检查是否为闰年的接口,这个接口一般由公共日期类库提供

所以我们在做日历相关的过程中,完全可以整理一套日期的API出来,这也是今天一个任务

日期操作类库

这里首先给出两个接口,一个判断是否为闰年,一个判断一个月有多少天

  1. var dateUtil = {
  2. // @description 是否为闰年
  3. // @param year {num} 可能是年份或者为一个date时间
  4. // @return {boolean} 返回值
  5. isLeapYear: function (year) {
  6. //传入为时间格式需要处理
  7. if ((typeof year == 'object') && (year instanceof Date)) year = year.getFullYear()
  8. if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true;
  9. else return false;
  10. },
  11.  
  12. // @description 获取一个月份的天数
  13. // @param year {num} 可能是年份或者为一个date时间
  14. // @param year {num} 月份
  15. // @return {num} 返回天数
  16. getDaysOfMonth: function (year, month) {
  17. if ((typeof year == 'object') && (year instanceof Date)) {
  18. month = year.getmonth() + 1; //注意此处月份要加1
  19. year = year.getFullYear();
  20. }
  21. return [31, dateUtil.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
  22. }
  23. };

官方的getDay即可返回某天为星期几

0-6:星期天-星期六

所以,理论上我们给一个日期,就可以获得那一天的dom结构了,我们来试试,获取本月的日历数据

这里我们需要新增一个API告诉我们一年中的某一个月是由周几开始的

  1. // @description 获取一个月份1号是星期几,注意此时的月份传入时需要自主减一
  2. // @param year {num} 可能是年份或者为一个date时间
  3. // @param year {num} 月份
  4. // @return {num} 当月一号为星期几0-6
  5. getBeginDayOfMouth: function (year, month) {
  6. if ((typeof year == 'object') && (year instanceof Date)) {
  7. month = year.getMonth(); //注意此处月份要加1
  8. year = year.getFullYear();
  9. }
  10. var d = new Date(year, month, 1);
  11. return d.getDay();
  12. }

渲染dom

这个时候我们尝试生成我们的dom结构就出来了:

  1. <style type="text/css">
  2. ul, li { padding: 0; margin: 0; }
  3. .cui_calendar, .cui_week { list-style: none; }
  4. .cui_calendar li, .cui_week li { float: left; width: 14%; overflow: hidden; padding: 4px 0; text-align: center; }
  5. </style>

我们这里做一次简单的封装后,开始引入模板相关的东西,于是最后形成的东西:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title></title>
  5. <style type="text/css">
  6. ul, li { padding: 0; margin: 0; }
  7. .cui_calendar, .cui_week { list-style: none; }
  8. .cui_calendar li, .cui_week li { float: left; width: 14%; overflow: hidden; padding: 4px 0; text-align: center; }
  9. </style>
  10. </head>
  11. <body>
  12. <script src="zepto.js" type="text/javascript"></script>
  13. <script src="underscore-min.js" type="text/javascript"></script>
  14. <script type="text/javascript">
  15.  
  16. var arr = [];
  17. var slice = arr.slice;
  18. /**
  19. * @description inherit方法,js的继承,默认为两个参数
  20. * @param {function} supClass 可选,要继承的类
  21. * @param {object} subProperty 被创建类的成员
  22. * @return {function} 被创建的类
  23. */
  24. var inherit = function () {
  25.  
  26. // @description 参数检测,该继承方法,只支持一个参数创建类,或者两个参数继承类
  27. if (arguments.length == 0 || arguments.length > 2) throw '参数错误';
  28.  
  29. var parent = null;
  30.  
  31. // @description 将参数转换为数组
  32. var properties = slice.call(arguments);
  33.  
  34. // @description 如果第一个参数为类(function),那么就将之取出
  35. if (typeof properties[0] === 'function')
  36. parent = properties.shift();
  37. properties = properties[0];
  38.  
  39. // @description 创建新类用于返回
  40. function klass() {
  41. this.initialize.apply(this, arguments);
  42. }
  43.  
  44. klass.superclass = parent;
  45. klass.subclasses = [];
  46.  
  47. if (parent) {
  48. // @description 中间过渡类,防止parent的构造函数被执行
  49. var subclass = function () { };
  50. subclass.prototype = parent.prototype;
  51. klass.prototype = new subclass;
  52. parent.subclasses.push(klass);
  53. }
  54.  
  55. var ancestor = klass.superclass && klass.superclass.prototype;
  56. for (var k in properties) {
  57. var value = properties[k];
  58.  
  59. //满足条件就重写
  60. if (ancestor && typeof value == 'function') {
  61. var argslist = /^\s*function\s*\(([^\(\)]*?)\)\s*?\{/i.exec(value.toString())[1].replace(/\s/i, '').split(',');
  62. //只有在第一个参数为$super情况下才需要处理(是否具有重复方法需要用户自己决定)
  63. if (argslist[0] === '$super' && ancestor[k]) {
  64. value = (function (methodName, fn) {
  65. return function () {
  66. var scope = this;
  67. var args = [function () {
  68. return ancestor[methodName].apply(scope, arguments);
  69. } ];
  70. return fn.apply(this, args.concat(slice.call(arguments)));
  71. };
  72. })(k, value);
  73. }
  74. }
  75.  
  76. klass.prototype[k] = value;
  77. }
  78.  
  79. if (!klass.prototype.initialize)
  80. klass.prototype.initialize = function () { };
  81.  
  82. klass.prototype.constructor = klass;
  83.  
  84. return klass;
  85. };
  86.  
  87. var dateUtil = {
  88. // @description 是否为闰年
  89. // @param year {num} 可能是年份或者为一个date时间
  90. // @return {boolean} 返回值
  91. isLeapYear: function (year) {
  92. //传入为时间格式需要处理
  93. if ((typeof year == 'object') && (year instanceof Date)) year = year.getFullYear()
  94. if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true;
  95. else return false;
  96. },
  97.  
  98. // @description 获取一个月份的天数
  99. // @param year {num} 可能是年份或者为一个date时间
  100. // @param year {num} 月份
  101. // @return {num} 返回天数
  102. getDaysOfMonth: function (year, month) {
  103. if ((typeof year == 'object') && (year instanceof Date)) {
  104. month = year.getMonth(); //注意此处月份要加1,所以我们要减一
  105. year = year.getFullYear();
  106. }
  107. return [31, dateUtil.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
  108. },
  109.  
  110. // @description 获取一个月份1号是星期几,注意此时的月份传入时需要自主减一
  111. // @param year {num} 可能是年份或者为一个date时间
  112. // @param year {num} 月份
  113. // @return {num} 当月一号为星期几0-6
  114. getBeginDayOfMouth: function (year, month) {
  115. if ((typeof year == 'object') && (year instanceof Date)) {
  116. month = year.getMonth(); //注意此处月份要加1
  117. year = year.getFullYear();
  118. }
  119. var d = new Date(year, month, 1);
  120. return d.getDay();
  121. }
  122. };
  123.  
  124. var Calendar = inherit({
  125. initialize: function () {
  126. this.dateObj = new Date();
  127. this.rootBox = $('body');
  128.  
  129. //星期项目模板
  130. this.weekDayItemTmpt = "<li><%=['日', '一', '二', '三', '四', '五', '六'][day] %></li>";
  131. //星期包裹层模板,传入今天星期几,内部怎么实现自己来
  132. this.weekDayTmpt = '<ul class="cui_week"><%for(var day = 0; day < 7; day++) { %> ' + this.weekDayItemTmpt + ' <%} %></ul>';
  133.  
  134. //各个单元格的模板,可以重写
  135. this.itemTmpt = '<li class="cui_calendar_item"><%=day %></li>';
  136. //无效项目模板
  137. this.invalidTmpt = '<li class="cui_invalid"></li>';
  138.  
  139. //月份模板,给定当前年月,以及天数,第一天星期几,让用户自己构造月度日历模板
  140. this.mouthTmpt = [
  141. '<ul class="cui_calendar">',
  142. '<% for(var i = 0; i < _beginWeek; i++) { %>',
  143. this.invalidTmpt,
  144. '<% } %>',
  145. '<% for(i = 0; i < days; i++) { %>',
  146. '<% day = i + 1; %>',
  147. this.itemTmpt,
  148. '<% } %>',
  149. '</ul>'
  150. ].join('');
  151.  
  152. this._initDom();
  153. },
  154.  
  155. _initDom: function () {
  156. var d = this.dateObj;
  157. //获取天数
  158. var days = dateUtil.getDaysOfMonth(d);
  159. //获取那个月第一天时星期几
  160. var _beginWeek = dateUtil.getBeginDayOfMouth(d);
  161.  
  162. var weekDom = _.template(this.weekDayTmpt)();
  163. var calendarDom = _.template(this.mouthTmpt, {
  164. _beginWeek: _beginWeek,
  165. days: days
  166. });
  167. this.rootBox.append(weekDom);
  168. this.rootBox.append(calendarDom);
  169. }
  170. });
  171. var c = new Calendar();
  172. </script>
  173. </body>
  174. </html>
  1. var Calendar = inherit({
  2. initialize: function () {
  3. this.dateObj = new Date();
  4. this.rootBox = $('body');
  5.  
  6. //星期项目模板
  7. this.weekDayItemTmpt = "<li><%=['日', '一', '二', '三', '四', '五', '六'][day] %></li>";
  8. //星期包裹层模板,传入今天星期几,内部怎么实现自己来
  9. this.weekDayTmpt = '<ul class="cui_week"><%for(var day = 0; day < 7; day++) { %> ' + this.weekDayItemTmpt + ' <%} %></ul>';
  10.  
  11. //各个单元格的模板,可以重写
  12. this.itemTmpt = '<li class="cui_calendar_item"><%=day %></li>';
  13. //无效项目模板
  14. this.invalidTmpt = '<li class="cui_invalid"></li>';
  15.  
  16. //月份模板,给定当前年月,以及天数,第一天星期几,让用户自己构造月度日历模板
  17. this.mouthTmpt = [
  18. '<ul class="cui_calendar">',
  19. '<% for(var i = 0; i < _beginWeek; i++) { %>',
  20. this.invalidTmpt,
  21. '<% } %>',
  22. '<% for(i = 0; i < days; i++) { %>',
  23. '<% day = i + 1; %>',
  24. this.itemTmpt,
  25. '<% } %>',
  26. '</ul>'
  27. ].join('');
  28.  
  29. this._initDom();
  30. },
  31.  
  32. _initDom: function () {
  33. var d = this.dateObj;
  34. //获取天数
  35. var days = dateUtil.getDaysOfMonth(d);
  36. //获取那个月第一天时星期几
  37. var _beginWeek = dateUtil.getBeginDayOfMouth(d);
  38.  
  39. var weekDom = _.template(this.weekDayTmpt)();
  40. var calendarDom = _.template(this.mouthTmpt, {
  41. _beginWeek: _beginWeek,
  42. days: days
  43. });
  44. this.rootBox.append(weekDom);
  45. this.rootBox.append(calendarDom);
  46. }
  47. });

这里将许多可能定制化的东西以模板的方式提了出来,比如我们的week,比如我们的月份模板,这里各个业务同事可以分别按照自己的需求进行扩展

这里定制的粒度完全由开发人员决定,他可以只定制各个项目,或者定制整个月份的模板,当然,我们这里需要传入的参数还不够,还需要增加

扩展

比如,我们要在每月上面显示当前是某年某月就需要更多的数据了,模板的扩展程度,很多时候取决于数据的完善性,这里年月属性我们都需要传入

所以我们模板处可以稍作修改就变成这个样子了:

  1. var c = new Calendar({
  2. mouthTmpt: [
  3. '<div style="overflow: hidden; width: 100%; text-align: center;"><%=year %>年-<%=mouth %>月</div>',
  4. '<ul class="cui_calendar">',
  5. '<% for(var i = 0; i < beginWeek; i++) { %>',
  6. '<li class="cui_invalid"></li>',
  7. '<% } %>',
  8. '<% for(i = 0; i < days; i++) { %>',
  9. '<% day = i + 1; %>',
  10. '<li class="cui_calendar_item"><%=day %></li>',
  11. '<% } %>',
  12. '</ul>'
  13. ].join('')

又或者,我们想让周末变成橙色的话,我们需要这么干,并且再加一点数据,我们直接告诉每项当前的年月日,所以他自己可以做很多判断

  1. var c = new Calendar({
  2. itemTmpt: '<li class="cui_calendar_item" <% var d = new Date(year, month, day);
    if(d.getDay() == 0 || d.getDay() == 6) %>style="color: orange; "<% %> ><%=day %></li>'
  3.  
  4. });

然后我们得将相关属性赋予dom结构的一个属性,方便后续操作,很多时候事件相关我们还是得依赖dom之间的映射,动态为每个dom增加data-date属性,年月日之间-分割

因为日模板可以被复写,所以这里需要一个规范,如果这个规范没有了,可能导致日期操作失效

我们知道日期的月份由0开始,我们现在是四月,所以对应的月份却应该是3月

代码分解

经过前面的学习,我们简单的日历原型其实应该出来了,现在要对其中代码做一些优化

PS:其实现在代码比较少,优化点不多,我们首先将构造星期与日历相关dom结构的两个方法分离出来

  1. _getWeekDom: function () {
  2. return _.template(this.weekDayTmpt)();
  3. },
  4. //description 获得某月的dom结构
  5. _getMonthDom: function (year, month) {
  6. var d = new Date(year, month);
  7. //description 获取天数
  8. var days = dateUtil.getDaysOfMonth(d);
  9. //description 获取那个月第一天时星期几
  10. var _beginWeek = dateUtil.getBeginDayOfMouth(d);
  11.  
  12. var weekDom = _.template(this.weekDayTmpt)();
  13. return _.template(this.mouthTmpt, {
  14. year: d.getFullYear(),
  15. month: d.getMonth(),
  16. beginWeek: _beginWeek,
  17. days: days
  18. });
  19. },
  20. init: function () {
  21. this.rootBox.append(this._getWeekDom());
  22. this.rootBox.append(this._getMonthDom(this.dateObj.getFullYear(), this.dateObj.getMonth()));
  23. }

其次,这里的dateObj比较关键,一旦出问题就会导致许多错误,所以我们最开始应该有一个验证的方法,验证是否是日期的方法当然该由dateUtil提供

这里不但需要验证是否为日期,还需要提供新的日期格式化方法,parseDate方法,用于转变常用日期字符串为日期

日期操作

首先验证日期我们简单一点

  1. isDate: function (d) {
  2. if ((typeof d == 'object') && (d instanceof Date)) return true;
  3. return false;
  4. },

然后是比较复杂的转换字符串为日期对象,以及转换日期对象为常用字符串

格式化日期字符串parse

这句话的思考是可以匹配各种我们认为是日期格式的字符串,我们只需要告诉年月日的格式化方式或者位置即可,比如以下几种

2014年4月20日、2014420、2014-4-20、2014 4 20、2041/4/20

  1. //将字符串转换为日期
  2. //支持格式y-m-d ymd (y m r)以及标准的
  3. parse: function (dateStr, formatStr) {
  4. if (typeof dateStr === 'undefined') return null;
  5. if (typeof formatStr === 'string') {
  6. //首先取得顺序相关字符串
  7. var arrStr = formatStr.replace(/[^ymd]/g, '').split('');
  8. if (!arrStr) return null;
  9. var formatStr = formatStr.replace('y', '(\\{b}{4})');
  10. var formatStr = formatStr.replace('m', '(\\{b}{1,2})');
  11. var formatStr = formatStr.replace('d', '(\\{b}{1,2})');
  12. var formatStr = formatStr.replace(/\{b\}/g, 'd');
  13.  
  14. var reg = new RegExp(formatStr, 'g');
  15. var arr = reg.exec(dateStr)
  16.  
  17. var dateObj = {};
  18. for (var i = 0, len = arrStr.length; i < len; i++) {
  19. dateObj[arrStr[i]] = arr[i + 1];
  20. }
  21. return new Date(dateObj['y'], dateObj['m'], dateObj['d']);
  22. }
  23. return null;
  24. },

因为楼主正则不是很好,上面的代码应该不是最优写法,基本调用方法如下:

  1. console.log( dateUtil.parse('2012-4-1', 'y-m-d'));
  2. console.log(dateUtil.parse('2/4/2014', 'd/m/y'));
  3. console.log(dateUtil.parse('2014 4 3', 'y m d'));
  4. console.log(dateUtil.parse('2014年4月4日', 'y年m月d日'));
  5. console.log(dateUtil.parse('2012-04-05', 'y-m-d'));
  6. console.log(dateUtil.parse('06/4/2014', 'd/m/y'));
  7. console.log(dateUtil.parse('2014 4 07', 'y m d'));
  8. console.log(dateUtil.parse('2014年4月08日', 'y年m月d日'));
  9.  
  10. //输出结果
  11. Tue May 01 2012 00:00:00 GMT+0800 (中国标准时间) 01.htm:229
  12. Fri May 02 2014 00:00:00 GMT+0800 (中国标准时间) 01.htm:230
  13. Sat May 03 2014 00:00:00 GMT+0800 (中国标准时间) 01.htm:231
  14. Sun May 04 2014 00:00:00 GMT+0800 (中国标准时间) 01.htm:232
  15. Sat May 05 2012 00:00:00 GMT+0800 (中国标准时间) 01.htm:233
  16. Tue May 06 2014 00:00:00 GMT+0800 (中国标准时间) 01.htm:234
  17. Wed May 07 2014 00:00:00 GMT+0800 (中国标准时间) 01.htm:235
  18. Thu May 08 2014 00:00:00 GMT+0800 (中国标准时间)

从结果来看,返回时正确的,若是有什么不对,就再说吧。。。。。。

格式化日期为字符串format

上面我们将特殊字符串转换为了日期,我们还得有个借口将日期格式化为需要的字符串

这个网上有一个很不错的方案,这里直接抄了。。。。。。

  1. console.log(dateUtil.format('YYYY年MM月DD日'));
  2. console.log(dateUtil.format('YYYY-MM-DD'));
  3.  
  4. 2014420 01.htm:251
  5. 2014-4-20

稍有不足便是没有进行1与01相关的选择,我们这里稍作修改,而且这里对我们上面的代码优化提出了方案,我们一并修改

  1. function formatDate(date, format) {
  2. if (arguments.length < 2 && !date.getTime) {
  3. format = date;
  4. date = new Date();
  5. }
  6. typeof format != 'string' && (format = 'YYYY年MM月DD日 hh时mm分ss秒');
  7. var week = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', '日', '一', '二', '三', '四', '五', '六'];
  8. return format.replace(/YYYY|YY|MM|DD|hh|mm|ss|星期|周|www|week/g, function(a) {
  9. switch (a) {
  10. case "YYYY": return date.getFullYear();
  11. case "YY": return (date.getFullYear()+"").slice(2);
  12. case "MM": return date.getMonth() + 1;
  13. case "DD": return date.getDate();
  14. case "hh": return date.getHours();
  15. case "mm": return date.getMinutes();
  16. case "ss": return date.getSeconds();
  17. case "星期": return "星期" + week[date.getDay() + 7];
  18. case "周": return "周" + week[date.getDay() + 7];
  19. case "week": return week[date.getDay()];
  20. case "www": return week[date.getDay()].slice(0,3);
  21. }
  22. });
  23. }
  1. format: function (date, formatStr) {
  2. if (arguments.length < 2 && !date.getTime) {
  3. format = date;
  4. date = new Date();
  5. }
  6. typeof format != 'string' && (format = 'Y年M月D日 H时F分S秒');
  7. return format.replace(/Y|y|M|m|D|d|H|h|F|f|S|s/g, function (a) {
  8. switch (a) {
  9. case "y": return (date.getFullYear() + "").slice(2);
  10. case "Y": return date.getFullYear();
  11. case "m": return date.getMonth() + 1;
  12. case "M": return dateUtil.formatNum(date.getMonth() + 1);
  13. case "d": return date.getDate();
  14. case "D": return dateUtil.formatNum(date.getDate());
  15. case "h": return date.getHours();
  16. case "H": return dateUtil.formatNum(date.getHours());
  17. case "f": return date.getMinutes();
  18. case "F": return dateUtil.formatNum(date.getMinutes());
  19. case "s": return date.getSeconds();
  20. case "S": return dateUtil.formatNum(date.getSeconds());
  21. }
  22. });
  23. },

由于这里月与分钟都是以m开头,这里会有问题,所以我这里可耻的将分改为F。。。。。。

对应日期处理工厂现在变成这个样子了

  1. var dateUtil = {
  2. formatNum: function (n) {
  3. if (n < 10) return '0' + n;
  4. return n;
  5. },
  6. //将字符串转换为日期
  7. //支持格式y-m-d ymd (y m r)以及标准的
  8. parse: function (dateStr, formatStr) {
  9. if (typeof dateStr === 'undefined') return null;
  10. if (typeof formatStr === 'string') {
  11. //首先取得顺序相关字符串
  12. var arrStr = formatStr.replace(/[^ymd]/g, '').split('');
  13. if (!arrStr && arrStr.length != 3) return null;
  14.  
  15. var formatStr = formatStr.replace(/y|m|d/g, function (k) {
  16. switch (k) {
  17. case 'y': return '(\\d{4})';
  18. case 'm': ;
  19. case 'd': return '(\\d{1,2})';
  20. }
  21. });
  22.  
  23. var reg = new RegExp(formatStr, 'g');
  24. var arr = reg.exec(dateStr)
  25.  
  26. var dateObj = {};
  27. for (var i = 0, len = arrStr.length; i < len; i++) {
  28. dateObj[arrStr[i]] = arr[i + 1];
  29. }
  30. return new Date(dateObj['y'], dateObj['m'], dateObj['d']);
  31. }
  32. return null;
  33. },
  34. //将日期格式化为字符串
  35. format: function (date, formatStr) {
  36. if (arguments.length < 2 && !date.getTime) {
  37. format = date;
  38. date = new Date();
  39. }
  40. typeof format != 'string' && (format = 'Y年M月D日 H时F分S秒');
  41. return format.replace(/Y|y|M|m|D|d|H|h|F|f|S|s/g, function (a) {
  42. switch (a) {
  43. case "y": return (date.getFullYear() + "").slice(2);
  44. case "Y": return date.getFullYear();
  45. case "m": return date.getMonth() + 1;
  46. case "M": return dateUtil.formatNum(date.getMonth() + 1);
  47. case "d": return date.getDate();
  48. case "D": return dateUtil.formatNum(date.getDate());
  49. case "h": return date.getHours();
  50. case "H": return dateUtil.formatNum(date.getHours());
  51. case "f": return date.getMinutes();
  52. case "F": return dateUtil.formatNum(date.getMinutes());
  53. case "s": return date.getSeconds();
  54. case "S": return dateUtil.formatNum(date.getSeconds());
  55. }
  56. });
  57. },
  58. // @description 是否为为日期对象,该方法可能有坑,使用需要慎重
  59. // @param year {num} 日期对象
  60. // @return {boolean} 返回值
  61. isDate: function (d) {
  62. if ((typeof d == 'object') && (d instanceof Date)) return true;
  63. return false;
  64. },
  65. // @description 是否为闰年
  66. // @param year {num} 可能是年份或者为一个date时间
  67. // @return {boolean} 返回值
  68. isLeapYear: function (year) {
  69. //传入为时间格式需要处理
  70. if (dateUtil.isDate(year)) year = year.getFullYear()
  71. if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true;
  72. else return false;
  73. },
  74.  
  75. // @description 获取一个月份的天数
  76. // @param year {num} 可能是年份或者为一个date时间
  77. // @param year {num} 月份
  78. // @return {num} 返回天数
  79. getDaysOfMonth: function (year, month) {
  80. if (dateUtil.isDate(year)) {
  81. month = year.getMonth(); //注意此处月份要加1,所以我们要减一
  82. year = year.getFullYear();
  83. }
  84. return [31, dateUtil.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
  85. },
  86.  
  87. // @description 获取一个月份1号是星期几,注意此时的月份传入时需要自主减一
  88. // @param year {num} 可能是年份或者为一个date时间
  89. // @param year {num} 月份
  90. // @return {num} 当月一号为星期几0-6
  91. getBeginDayOfMouth: function (year, month) {
  92. if ((typeof year == 'object') && (year instanceof Date)) {
  93. month = year.getMonth(); //注意此处月份要加1
  94. year = year.getFullYear();
  95. }
  96. var d = new Date(year, month, 1);
  97. return d.getDay();
  98. }
  99. };

日期操作接口

既然是日期,一定会有日期项目的操作,我们这里需要提供一个接口将某一项交给用户操作

这个接口本身不是很难,比较烦的一个时期,就是这里传入的月份是否应该加1的问题

比如我们操作的明明是4月却要这样写2014-3-20,这个事情比较烦,所以建议传日期对象算了

  1. //操作每一个日期
  2. handleDay: function (dateStr, fn) {
  3. if (dateUtil.isDate(dateStr)) dateStr = dateUtil.format(dateStr, 'Y-m-d');
  4. var el = this.root.find('[data-date="' + dateStr + '"]');
  5.  
  6. if (typeof fn == 'function') fn(el, dateUtil.parse(dateStr, 'y-m-d'), this);
  7.  
  8. }
  9.  
  10. var c = new Calendar({ });
  11. c.handleDay(new Date(), function (el, date, calendar) {
  12. el.html('今天');
  13. });

这个的好处是什么呢,若是我们有一个需求需要修改某一个星期,或者几个连续工作日的属性便可以如此操作,但是需要操作每个dom结构似乎有点不舒服

比如我们现在要去这个月周三高亮显示,这个时候我们的日历还需要提供一个接口,让外面可以对自己做遍历操作

遍历操作结构eachDay

  1. eachDay: function (fn) {
  2. var els = this.root.find('[data-date]');
  3. if (typeof fn == 'function') fn(els);
  4. }
  5.  
  6. c.eachDay(function (els) {
  7. $.each(els, function (i, el) {
  8. el = $(el);
  9. el.html(el.html() + '号');
  10. });
  11. });
  12.  
  13. c.handleDay(new Date(), function (el, date, calendar) {
  14. el.html('今天');
  15. });

这里依旧有一个问题:DOM操作太多了,这个方案有问题,所以我们还得优化

事件绑定

待续......

结语

今天太晚了,我们下次继续

【UI插件】开发一个简单日历插件(上)的更多相关文章

  1. 重新想象 Windows 8 Store Apps (64) - 后台任务: 开发一个简单的后台任务

    [源码下载] 重新想象 Windows 8 Store Apps (64) - 后台任务: 开发一个简单的后台任务 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 后 ...

  2. django学习-11.开发一个简单的醉得意菜单和人均支付金额查询页面

    1.前言 刚好最近跟技术部门的[产品人员+UI人员+测试人员],组成了一桌可以去公司楼下醉得意餐厅吃饭的小team. 所以为了实现这些主要点餐功能: 提高每天中午点餐效率,把点餐时间由20分钟优化为1 ...

  3. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

  4. Cocos2d-x-Lua 开发一个简单的游戏(记数字步进白色块状)

    Cocos2d-x-Lua 开发一个简单的游戏(记数字步进白色块状) 本篇博客来给大家介绍怎样使用Lua这门语言来开发一个简单的小游戏-记数字踩白块. 游戏的流程是这种:在界面上生成5个数1~5字并显 ...

  5. Python开发一个简单的BBS论坛

    项目:开发一个简单的BBS论坛 需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可 ...

  6. 作业1开发一个简单的python计算器

    开发一个简单的python计算器 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568 ...

  7. Java实现一个简单的文件上传案例

    Java实现一个简单的文件上传案例 实现流程: 1.客户端从硬盘读取文件数据到程序中 2.客户端输出流,写出文件到服务端 3.服务端输出流,读取文件数据到服务端中 4.输出流,写出文件数据到服务器硬盘 ...

  8. 开发一个简单的babel插件

    前言 对于前端开发而言,babel肯定是再熟悉不过了,工作中肯定会用到.除了用作转换es6和jsx的工具之外,个人感觉babel基于抽象语法树的插件机制,给我们提供了更多的可能.关于babel相关概念 ...

  9. [MEF插件式开发] 一个简单的例子

    偶然在博客园中了解到这种技术,顺便学习了几天. 以下是搜索到一些比较好的博文供参考: MEF核心笔记 <MEF程序设计指南>博文汇总 先上效果图 一.新建解决方案 开始新建一个解决方案Me ...

随机推荐

  1. [译]AngularJS $apply, $digest, 和$evalAsync的比较

    原文:The differences between AngularJS $apply, $digest, and $evalAsync 你是不是也常在想AngularJS $apply, $dige ...

  2. 复习sql第三次

    1.层次型数据库以"树"结构表示数据库中数据间的关系:网状型以"图"结构表示数据库中数据间的关系:关系型数据库以"二维表"结构表示数据库中数 ...

  3. .net 网络编程

    1.首先说下计算机网络中的TCP/IP参考模型 TCP/IP把网络分为5层,每一层负责完成不同的功能 1)应用层:传输报文,提供各种网络应用,有FTP.SMTP.HTTP等协议 2)运输层:传输报文段 ...

  4. Sql Server系列:日期和时间函数

    1. 获取系统当前日期函数GETDATE() GETDATE()函数用于返回当前数据库系统的日期和时间,返回值的类型为datetime. SELECT GETDATE() 2. 返回UTC日期的函数G ...

  5. Windows phone 全景视图

    Windows phone 全景视图下为了实现可以上下滑动需要使用listbox. 需要的布局什么的,在listbox中填写 <ListBox Name="ListBox_new&qu ...

  6. mysql数据库学习目录

    前面的话 对于前端工程师来说,数据库并不是主要技能点,但是基本的增删改查操作还是需要了解的.小火柴将mysql数据库的学习记录整理如下 目录  前端学数据库之基础操作 前端学数据库之数据类型 前端学数 ...

  7. java 导出数据为word文档(保持模板格式)

    导出数据到具体的word文档里面,word有一定的格式,需要保持不变 这里使用freemarker来实现: ①:设计好word文档格式,需要用数据填充的地方用便于识别的长字符串替换  如  aaaaa ...

  8. Web前端上万字的知识总结

    下面是自己学HTML+DIV+CSS+JS时的学习笔记,给大家分享以下,相互学习.大二时候寒假在家无聊的时候想做点事,总结了一下web前端基础的东西,下面的每个字都是自己手敲的. 1.<html ...

  9. android UI 仿 win 8 模块化 标题,并实现 可长按拖动交换图片位置、可点击,且伴随动画特效

    转载请声明出处,谢谢!http://www.cnblogs.com/linguanh/ 先上效果图,给大家个直观效果,后上实现代码:  ->  ->->  ok,现在简单说下我上面的 ...

  10. 回忆:#define的用法

    ANSI C规定:#前可以有空格或者tab,#和指令其余部分之间也可以有空格,可以出现在任何地方,作用域从定义处到文件结尾. 因为预处理开始前,系统会删除反斜线和换行符的组合,故可以把指令扩展到几个物 ...