【UI插件】開發一個簡單日曆插件(上)

前言

最近開始整理咱們的單頁應用框架了,雖然可能比不上MVVM模式的開發效率,也可能沒有Backbone框架模塊清晰,可是好歹也是本身開發出來javascript

並且也用於了這麼多頻道的東西,若是沒有總結,沒有整理,沒有開源就太惋惜了......因此最近開始整理框架相關的東西,爭取抽象一點東西出來css

框架出來還須要一點時間,可是框架會須要相關的UI庫,這個東西能夠先有思路,最後再根據框架作一點調整吧html

日曆對於UI插件而言仍是比較難的,裏面涉及到的東西不少,就陰曆與陽曆一塊就有不少東西,而後涉及到不少算法,其中節日的設置更是有必定動態性java

各類各樣的需求也是莫名其妙,因此咱們今天便來實現一個簡單的日曆插件吧,固然他的主要應用場景仍是單頁應用算法

構思

首先,咱們這裏用這套東西實現繼承api

var arr = [];
var slice = arr.slice;

function create() {
  if (arguments.length == 0 || arguments.length > 2) throw '參數錯誤';

  var parent = null;
  //將參數轉換爲數組
  var properties = slice.call(arguments);

  //若是第一個參數爲類(function),那麼就將之取出
  if (typeof properties[0] === 'function')
    parent = properties.shift();
  properties = properties[0];

  function klass() {
    this.initialize.apply(this, arguments);
  }

  klass.superclass = parent;
  klass.subclasses = [];

  if (parent) {
    var subclass = function () { };
    subclass.prototype = parent.prototype;
    klass.prototype = new subclass;
    parent.subclasses.push(klass);
  }

  var ancestor = klass.superclass && klass.superclass.prototype;
  for (var k in properties) {
    var value = properties[k];

    //知足條件就重寫
    if (ancestor && typeof value == 'function') {
      var argslist = /^\s*function\s*\(([^\(\)]*?)\)\s*?\{/i.exec(value.toString())[1].replace(/\s/i, '').split(',');
      //只有在第一個參數爲$super狀況下才須要處理(是否具備重複方法須要用戶本身決定)
      if (argslist[0] === '$super' && ancestor[k]) {
        value = (function (methodName, fn) {
          return function () {
            var scope = this;
            var args = [function () {
              return ancestor[methodName].apply(scope, arguments);
            } ];
            return fn.apply(this, args.concat(slice.call(arguments)));
          };
        })(k, value);
      }
    }

    klass.prototype[k] = value;
  }

  if (!klass.prototype.initialize)
    klass.prototype.initialize = function () { };

  klass.prototype.constructor = klass;

  return klass;
}

其次,咱們的日曆作出來應該是可定製化的,可定製化的粒度控制到每個單元格,意思是每個單元格是可操做的數組

這個時候最好的解決辦法就是模板,而且釋放一個操做某個日期的接口,好比咱們如今要實現陰曆節日或者陽曆節日徹底是實現抽象的日曆,這樣能夠最大的提升擴展性app

因此,咱們這裏的第一步是實現一個最基本的抽象日曆框架

abstract.calendar

像日曆這類插件,我首先仍是想到用表格來作,可是CSS3的出現也能讓咱們的代碼很好的實現,因此我這裏使用li作,具體實現咱們後面再說,咱們要完成的第一個事情是dom

渲染當月

咱們作的第一個事情是給一個日期,而後當月的數據便出來了,好比咱們這裏給的是20140420,就是當前日期,而後便須要造成這個月的日期,這裏就涉及到一連串東西了

解決這個問題,咱們須要第一個api,算出給定日期一共有多少天,第二步即是排列第一個日期爲星期幾便可

衆所周知,計算月份天數時候有一個例外的狀況即是閏年的二月,因此咱們須要檢查是否爲閏年的接口,這個接口通常由公共日期類庫提供

因此咱們在作日曆相關的過程當中,徹底能夠整理一套日期的API出來,這也是今天一個任務

日期操做類庫

這裏首先給出兩個接口,一個判斷是否爲閏年,一個判斷一個月有多少天

var dateUtil = {
  // @description 是否爲閏年
  // @param year {num} 多是年份或者爲一個date時間
  // @return {boolean} 返回值
  isLeapYear: function (year) {
    //傳入爲時間格式須要處理
    if ((typeof year == 'object') && (year instanceof Date)) year = year.getFullYear()
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true;
    else return false;
  },

  // @description 獲取一個月份的天數
  // @param year {num} 多是年份或者爲一個date時間
  // @param year {num} 月份
  // @return {num} 返回天數
  getDaysOfMonth: function (year, month) {
    if ((typeof year == 'object') && (year instanceof Date)) {
      month = year.getmonth() + 1; //注意此處月份要加1
      year = year.getFullYear();
    }
    return [31, dateUtil.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
  }
};

官方的getDay便可返回某天爲星期幾

0-6:星期天-星期六

因此,理論上咱們給一個日期,就能夠得到那一天的dom結構了,咱們來試試,獲取本月的日曆數據

這裏咱們須要新增一個API告訴咱們一年中的某一個月是由周幾開始的

// @description 獲取一個月份1號是星期幾,注意此時的月份傳入時須要自主減一
// @param year {num} 多是年份或者爲一個date時間
// @param year {num} 月份
// @return {num} 當月一號爲星期幾0-6
getBeginDayOfMouth: function (year, month) {
  if ((typeof year == 'object') && (year instanceof Date)) {
    month = year.getMonth(); //注意此處月份要加1
    year = year.getFullYear();
  }
  var d = new Date(year, month, 1);
  return d.getDay();
}

渲染dom

這個時候咱們嘗試生成咱們的dom結構就出來了:

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

咱們這裏作一次簡單的封裝後,開始引入模板相關的東西,因而最後造成的東西:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <style type="text/css">
    ul, li { padding: 0; margin: 0; }
    .cui_calendar, .cui_week { list-style: none; }
    .cui_calendar li, .cui_week li { float: left; width: 14%; overflow: hidden; padding: 4px 0; text-align: center; }
  </style>
</head>
<body>
  <script src="zepto.js" type="text/javascript"></script>
  <script src="underscore-min.js" type="text/javascript"></script>
  <script type="text/javascript">

  var arr = [];
var slice = arr.slice;
/**
* @description inherit方法,js的繼承,默認爲兩個參數
* @param {function} supClass 可選,要繼承的類
* @param {object} subProperty 被建立類的成員
* @return {function} 被建立的類
*/
var inherit = function () {

  // @description 參數檢測,該繼承方法,只支持一個參數建立類,或者兩個參數繼承類
  if (arguments.length == 0 || arguments.length > 2) throw '參數錯誤';

  var parent = null;

  // @description 將參數轉換爲數組
  var properties = slice.call(arguments);

  // @description 若是第一個參數爲類(function),那麼就將之取出
  if (typeof properties[0] === 'function')
    parent = properties.shift();
  properties = properties[0];

  // @description 建立新類用於返回
  function klass() {
    this.initialize.apply(this, arguments);
  }

  klass.superclass = parent;
  klass.subclasses = [];

  if (parent) {
    // @description 中間過渡類,防止parent的構造函數被執行
    var subclass = function () { };
    subclass.prototype = parent.prototype;
    klass.prototype = new subclass;
    parent.subclasses.push(klass);
  }

  var ancestor = klass.superclass && klass.superclass.prototype;
  for (var k in properties) {
    var value = properties[k];

    //知足條件就重寫
    if (ancestor && typeof value == 'function') {
      var argslist = /^\s*function\s*\(([^\(\)]*?)\)\s*?\{/i.exec(value.toString())[1].replace(/\s/i, '').split(',');
      //只有在第一個參數爲$super狀況下才須要處理(是否具備重複方法須要用戶本身決定)
      if (argslist[0] === '$super' && ancestor[k]) {
        value = (function (methodName, fn) {
          return function () {
            var scope = this;
            var args = [function () {
              return ancestor[methodName].apply(scope, arguments);
            } ];
            return fn.apply(this, args.concat(slice.call(arguments)));
          };
        })(k, value);
      }
    }

    klass.prototype[k] = value;
  }

  if (!klass.prototype.initialize)
    klass.prototype.initialize = function () { };

  klass.prototype.constructor = klass;

  return klass;
};


    var dateUtil = {
      // @description 是否爲閏年
      // @param year {num} 多是年份或者爲一個date時間
      // @return {boolean} 返回值
      isLeapYear: function (year) {
        //傳入爲時間格式須要處理
        if ((typeof year == 'object') && (year instanceof Date)) year = year.getFullYear()
        if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true;
        else return false;
      },

      // @description 獲取一個月份的天數
      // @param year {num} 多是年份或者爲一個date時間
      // @param year {num} 月份
      // @return {num} 返回天數
      getDaysOfMonth: function (year, month) {
        if ((typeof year == 'object') && (year instanceof Date)) {
          month = year.getMonth(); //注意此處月份要加1,因此咱們要減一
          year = year.getFullYear();
        }
        return [31, dateUtil.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
      },

      // @description 獲取一個月份1號是星期幾,注意此時的月份傳入時須要自主減一
      // @param year {num} 多是年份或者爲一個date時間
      // @param year {num} 月份
      // @return {num} 當月一號爲星期幾0-6
      getBeginDayOfMouth: function (year, month) {
        if ((typeof year == 'object') && (year instanceof Date)) {
          month = year.getMonth(); //注意此處月份要加1
          year = year.getFullYear();
        }
        var d = new Date(year, month, 1);
        return d.getDay();
      }
    };

    var Calendar = inherit({
      initialize: function () {
        this.dateObj = new Date();
        this.rootBox = $('body');
       
        //星期項目模板
        this.weekDayItemTmpt = "<li><%=['日', '一', '二', '三', '四', '五', '六'][day] %></li>";
        //星期包裹層模板,傳入今天星期幾,內部怎麼實現本身來
        this.weekDayTmpt = '<ul class="cui_week"><%for(var day = 0; day < 7; day++) { %> ' + this.weekDayItemTmpt + ' <%} %></ul>';

        //各個單元格的模板,能夠重寫
        this.itemTmpt = '<li class="cui_calendar_item"><%=day %></li>';
        //無效項目模板
        this.invalidTmpt = '<li class="cui_invalid"></li>';

        //月份模板,給定當前年月,以及天數,第一天星期幾,讓用戶本身構造月度日曆模板
        this.mouthTmpt = [
          '<ul class="cui_calendar">',
            '<% for(var i = 0; i < _beginWeek; i++) { %>',
              this.invalidTmpt,
            '<% } %>',
            '<% for(i = 0; i < days; i++) { %>',
              '<% day = i + 1; %>',
                this.itemTmpt,
            '<% } %>',
          '</ul>'
        ].join('');

        this._initDom();
      },

      _initDom: function () {
        var d = this.dateObj;
        //獲取天數
        var days = dateUtil.getDaysOfMonth(d);
        //獲取那個月第一天時星期幾
        var _beginWeek = dateUtil.getBeginDayOfMouth(d);

        var weekDom = _.template(this.weekDayTmpt)();
        var calendarDom = _.template(this.mouthTmpt, {
          _beginWeek: _beginWeek,
          days: days
        });
        this.rootBox.append(weekDom); 
        this.rootBox.append(calendarDom);
      }
    });
    var c = new Calendar();
  </script>
</body>
</html>
View Code
var Calendar = inherit({
  initialize: function () {
    this.dateObj = new Date();
    this.rootBox = $('body');
       
    //星期項目模板
    this.weekDayItemTmpt = "<li><%=['日', '一', '二', '三', '四', '五', '六'][day] %></li>";
    //星期包裹層模板,傳入今天星期幾,內部怎麼實現本身來
    this.weekDayTmpt = '<ul class="cui_week"><%for(var day = 0; day < 7; day++) { %> ' + this.weekDayItemTmpt + ' <%} %></ul>';

    //各個單元格的模板,能夠重寫
    this.itemTmpt = '<li class="cui_calendar_item"><%=day %></li>';
    //無效項目模板
    this.invalidTmpt = '<li class="cui_invalid"></li>';

    //月份模板,給定當前年月,以及天數,第一天星期幾,讓用戶本身構造月度日曆模板
    this.mouthTmpt = [
      '<ul class="cui_calendar">',
        '<% for(var i = 0; i < _beginWeek; i++) { %>',
          this.invalidTmpt,
        '<% } %>',
        '<% for(i = 0; i < days; i++) { %>',
          '<% day = i + 1; %>',
            this.itemTmpt,
        '<% } %>',
      '</ul>'
    ].join('');

    this._initDom();
  },

  _initDom: function () {
    var d = this.dateObj;
    //獲取天數
    var days = dateUtil.getDaysOfMonth(d);
    //獲取那個月第一天時星期幾
    var _beginWeek = dateUtil.getBeginDayOfMouth(d);

    var weekDom = _.template(this.weekDayTmpt)();
    var calendarDom = _.template(this.mouthTmpt, {
      _beginWeek: _beginWeek,
      days: days
    });
    this.rootBox.append(weekDom); 
    this.rootBox.append(calendarDom);
  }
});

這裏將許多可能定製化的東西以模板的方式提了出來,好比咱們的week,好比咱們的月份模板,這裏各個業務同事能夠分別按照本身的需求進行擴展

這裏定製的粒度徹底由開發人員決定,他能夠只定制各個項目,或者定製整個月份的模板,固然,咱們這裏須要傳入的參數還不夠,還須要增長

擴展

好比,咱們要在每個月上面顯示當前是某年某月就須要更多的數據了,模板的擴展程度,不少時候取決於數據的完善性,這裏年月屬性咱們都須要傳入

因此咱們模板處能夠稍做修改就變成這個樣子了:

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

又或者,咱們想讓週末變成橙色的話,咱們須要這麼幹,而且再加一點數據,咱們直接告訴每項當前的年月日,因此他本身能夠作不少判斷

var c = new Calendar({
  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>' });

而後咱們得將相關屬性賦予dom結構的一個屬性,方便後續操做,不少時候事件相關咱們仍是得依賴dom之間的映射,動態爲每一個dom增長data-date屬性,年月日之間-分割

由於日模板能夠被複寫,因此這裏須要一個規範,若是這個規範沒有了,可能致使日期操做失效

 

咱們知道日期的月份由0開始,咱們如今是四月,因此對應的月份卻應該是3月

代碼分解

通過前面的學習,咱們簡單的日曆原型其實應該出來了,如今要對其中代碼作一些優化

PS:其實如今代碼比較少,優化點很少,咱們首先將構造星期與日曆相關dom結構的兩個方法分離出來

_getWeekDom: function () {
  return _.template(this.weekDayTmpt)();
},
//description 得到某月的dom結構
_getMonthDom: function (year, month) {
  var d = new Date(year, month);
  //description 獲取天數
  var days = dateUtil.getDaysOfMonth(d);
  //description 獲取那個月第一天時星期幾
  var _beginWeek = dateUtil.getBeginDayOfMouth(d);

  var weekDom = _.template(this.weekDayTmpt)();
  return _.template(this.mouthTmpt, {
    year: d.getFullYear(),
    month: d.getMonth(),
    beginWeek: _beginWeek,
    days: days
  });
},
init: function () {
  this.rootBox.append(this._getWeekDom());
  this.rootBox.append(this._getMonthDom(this.dateObj.getFullYear(), this.dateObj.getMonth()));
}

其次,這裏的dateObj比較關鍵,一旦出問題就會致使許多錯誤,因此咱們最開始應該有一個驗證的方法,驗證是不是日期的方法固然該由dateUtil提供

這裏不但須要驗證是否爲日期,還須要提供新的日期格式化方法,parseDate方法,用於轉變經常使用日期字符串爲日期

日期操做

首先驗證日期咱們簡單一點

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

而後是比較複雜的轉換字符串爲日期對象,以及轉換日期對象爲經常使用字符串

格式化日期字符串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 },

由於樓主正則不是很好,上面的代碼應該不是最優寫法,基本調用方法以下:

console.log( dateUtil.parse('2012-4-1', 'y-m-d'));
console.log(dateUtil.parse('2/4/2014', 'd/m/y'));
console.log(dateUtil.parse('2014 4 3', 'y m d'));
console.log(dateUtil.parse('2014年4月4日', 'y年m月d日'));
console.log(dateUtil.parse('2012-04-05', 'y-m-d'));
console.log(dateUtil.parse('06/4/2014', 'd/m/y'));
console.log(dateUtil.parse('2014 4 07', 'y m d'));
console.log(dateUtil.parse('2014年4月08日', 'y年m月d日'));

//輸出結果
Tue May 01 2012 00:00:00 GMT+0800 (中國標準時間) 01.htm:229
Fri May 02 2014 00:00:00 GMT+0800 (中國標準時間) 01.htm:230
Sat May 03 2014 00:00:00 GMT+0800 (中國標準時間) 01.htm:231
Sun May 04 2014 00:00:00 GMT+0800 (中國標準時間) 01.htm:232
Sat May 05 2012 00:00:00 GMT+0800 (中國標準時間) 01.htm:233
Tue May 06 2014 00:00:00 GMT+0800 (中國標準時間) 01.htm:234
Wed May 07 2014 00:00:00 GMT+0800 (中國標準時間) 01.htm:235
Thu May 08 2014 00:00:00 GMT+0800 (中國標準時間) 

從結果來看,返回時正確的,如果有什麼不對,就再說吧。。。。。。

格式化日期爲字符串format

上面咱們將特殊字符串轉換爲了日期,咱們還得有個藉口將日期格式化爲須要的字符串

這個網上有一個很不錯的方案,這裏直接抄了。。。。。。

console.log(dateUtil.format('YYYY年MM月DD日'));
console.log(dateUtil.format('YYYY-MM-DD'));

2014年4月20日 01.htm:251
2014-4-20

稍有不足即是沒有進行1與01相關的選擇,咱們這裏稍做修改,並且這裏對咱們上面的代碼優化提出了方案,咱們一併修改

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

因爲這裏月與分鐘都是以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 };
View Code

日期操做接口

既然是日期,必定會有日期項目的操做,咱們這裏須要提供一個接口將某一項交給用戶操做

這個接口自己不是很難,比較煩的一個時期,就是這裏傳入的月份是否應該加1的問題

好比咱們操做的明明是4月卻要這樣寫2014-3-20,這個事情比較煩,因此建議傳日期對象算了

//操做每個日期
handleDay: function (dateStr, fn) {
  if (dateUtil.isDate(dateStr)) dateStr = dateUtil.format(dateStr, 'Y-m-d');
  var el = this.root.find('[data-date="' + dateStr + '"]');

  if (typeof fn == 'function') fn(el, dateUtil.parse(dateStr, 'y-m-d'), this);

}

var c = new Calendar({ });
c.handleDay(new Date(), function (el, date, calendar) {
  el.html('今天');
});

這個的好處是什麼呢,如果咱們有一個需求須要修改某一個星期,或者幾個連續工做日的屬性即可以如此操做,可是須要操做每一個dom結構彷佛有點不舒服

好比咱們如今要去這個月週三高亮顯示,這個時候咱們的日曆還須要提供一個接口,讓外面能夠對本身作遍歷操做

遍歷操做結構eachDay

eachDay: function (fn) {
  var els = this.root.find('[data-date]');
  if (typeof fn == 'function') fn(els);
}

c.eachDay(function (els) {
  $.each(els, function (i, el) {
    el = $(el);
    el.html(el.html() + '號');
  });
});

c.handleDay(new Date(), function (el, date, calendar) {
  el.html('今天');
});

這裏依舊有一個問題:DOM操做太多了,這個方案有問題,因此咱們還得優化

事件綁定

待續......

 

結語

今天太晚了,咱們下次繼續

相關文章
相關標籤/搜索