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


前言

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

而且也用於了這么多頻道的東西,如果沒有總結,沒有整理,沒有開源就太可惜了......所以最近開始整理框架相關的東西,爭取抽象一點東西出來

框架出來還需要一點時間,但是框架會需要相關的UI庫,這個東西可以先有思路,最后再根據框架做一點調整吧

日歷對於UI插件而言還是比較難的,里面涉及到的東西很多,就陰歷與陽歷一塊就有很多東西,然后涉及到很多算法,其中節日的設置更是有一定動態性

各種各樣的需求也是莫名其妙,所以我們今天便來實現一個簡單的日歷插件吧,當然他的主要應用場景還是單頁應用

構思

首先,我們這里用這套東西實現繼承

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;
}

其次,我們的日歷做出來應該是可定制化的,可定制化的粒度控制到每一個單元格,意思是每一個單元格是可操作的

這個時候最好的解決辦法就是模板,並且釋放一個操作某個日期的接口,比如我們現在要實現陰歷節日或者陽歷節日完全是實現抽象的日歷,這樣可以最大的提高擴展性

所以,我們這里的第一步是實現一個最基本的抽象日歷

abstract.calendar

像日歷這類插件,我首先還是想到用表格來做,但是CSS3的出現也能讓我們的代碼很好的實現,所以我這里使用li做,具體實現我們后面再說,我們要完成的第一個事情是

渲染當月

我們做的第一個事情是給一個日期,然后當月的數據便出來了,比如我們這里給的是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操作太多了,這個方案有問題,所以我們還得優化

事件綁定

待續......

 

結語

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


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com