自己写的一款jQuery日期选择器插件

基于jQuery写的一款日期选择器插件

兼容性

IE 6/7/8/9/10/11、Firefox、chrome、Safari、360、搜狗…

核心功能

  • 选择日期,翻月,翻年,选择年份区间。
  • 提供农历信息。
  • 特色功能:强容错、按键控制(支持up、down、left、right、Enter、Esc、pageUp、pageDown)、不限于绑定元素、自动定位弹框位置(自动判断上下左右距离选择合适位置弹框)、回调函数(自行处理返回的日期对象)
  • 代码特色:模板数据分离、模板引擎、DOM节点缓存、按需加载…

因为核心代码较多,就不大片大片的贴代码了。下面贴一些核心处理代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/**
* 日期选择类(依赖jquery)
* @Author: 张佳(jia58960)
* @Update: 2014/12/09
* @param {Object} target 必选,需要绑定的元素对象,要求是jQuery对象
* @param {Date} date 可选,初始化的时间
* @param {String} dateFormat 可选,日期显示格式。如"yyyy-MM-dd"
* @param {Function} callback 可选,选择日期后的回调
* @param {String} direction 可选,日历选择器的展示位置
*
* @examples
* new CalendarSelector({container:$("#demo"),date:'2014-11-12',dateFormat:'yyyy-MM-dd',callback});
*
* @returns {Object} dateObj 选中的日期对象
{String} dateString 指定格式的日期字符串(2014-12-09)
{Int} year 选中日期的年份 2014
{Int} month 选中日期的月份 12
{Int} date 选中日期的天 9
{String} lunarDate 选中日期日的农历表示 十八
{String} lunarMonth 选中日期月的农历表示 十月
{String} lunarYear 选中日期的年份表示 二零一四
{String} hsebYear 选中日期的干支年份表示 甲午年
*/

/**
* 封装好代码中可能会经常用到的工具类
* 提供一般的工具方法,抽离原因是希望日历选择器可以独立适用于各页面
*/
var Tools = {
Date: {
/**
* 将日期字符串转换成标准日期对象
* @param {String} str 时间字符串,如 2014/01/01 或者2014-1-1
*/
parse: function(str) {
if (/^\d{10}$/.test(str)) {
return new Date(str * 1000);
} else if (/^\d{13}$/.test(str)) {
return new Date(str * 1);
}
str = Tools.Text.trim(str);
var reg = /^(\d{4})[-\/](\d{1,2})[-\/](\d{1,2})$/;
var m = str.match(reg);
if (m) {
var year = m[1];
var month = parseInt(m[2] - 1, 10);
var day = parseInt(m[3], 10);
var hour = parseInt(m[4], 10);
return new Date(year, month, day);
} else {
return undefined;
}
},
/**
* 将时间对象格式化成指定格式的时间字符串
* @param {Date} date 时间对象
* @param {String} text 要格式化的文本格式
* @returns {String}
* @example
* Tools.Date.formatToString(new Date(), 'yyyy-MM-dd')
*/
formatToString: function(date, text) {
var reg = /yyyy|yy|M+|d+|S|w/g;
text = text.replace(reg, function(pattern) {
var result;
switch (pattern) {
case "yyyy":
result = date.getFullYear();
break;
case "yy":
result = date.getFullYear().toString().substring(2);
break;
case "M":
case "MM":
result = date.getMonth() + 1;
break;
case "dd":
case "d":
result = date.getDate();
break;
default:
result = "";
break;
}
if (pattern.length == 2 && result.toString().length == 1) {
result = "0" + result;
}
return result;
});
return text;
}
},

Text: {
/**
* 格式化字符串,提供数组和object两种方式(本质就是一个小的模板引擎)
* @example
* Tools.Text.format("hello,{name}",{name:"kitty"})
* Tools.Text.format("hello,{0}",["kitty"])
* @returns {String}
*/
format: function(str, arr) {
var reg;
if ($.isArray(arr)) {
reg = /\{([\d]+)\}/g;
} else {
reg = /\{([\w]+)\}/g;
}
return str.replace(reg, function($0, $1) {
var value = arr[$1];
if (value !== undefined) {
return value;
} else {
return "";
}
});
},

trim: function(str) {
var str = str,
str = str.replace(/^\s\s*/, ''),
ws = /\s/,
i = str.length;
while (ws.test(str.charAt(--i)));
return str.slice(0, i + 1);
}
}
}
/**
* 构造方法
*/
function CalendarSelector(options) {
this.initialize(options);
}
//直接在原型上扩展相关属性与方法,省去闭包的开销
CalendarSelector.prototype = {
/**
* 模板
*/
template: {
main: ['<div id="calendarS_{cid}" class="calendarSelector" style="position:absolute; z-index:999">',
'<div class="tips calendarPlugin" id="datepickerDays" style="display: block;">',
'<div class="calendarPlugin_top">',
'<a href="javascript:void(0);" class="i-calendarLeft" id="prevMonth"></a>',
'<a href="javascript:void(0);" class="calendarPlugin_time" id="dateDisplay">',
'</a>',
'<a href="javascript:void(0);" class="i-calendarRight" id="nextMonth"></a>',
'<input type="hidden" id="currentDate" />',
'</div>',
'<table class="calendarPluginTable">',
'<thead>',
'<tr><th>日</th><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th></tr>',
'</thead>',
'<tbody id="dateSelectorTBody"></tbody>',
'</table>',
'</div>',
'<div class="tips calendarPlugin" id="datepickerMonths" style="display: none;">',
'<div class="calendarPlugin_top">',
'<a href="javascript:void(0);" class="i-calendarLeft" id="prevYear"></a>',
'<a href="javascript:void(0);" class="calendarPlugin_timeS" id="yearDisplay">','</a>',
'<a href="javascript:void(0);" class="i-calendarRight" id="nextYear"></a>',
'</div>',
'<table class="calendarPluginTable_other">',
'<tbody id="monthSelectorTBody">',
'<tr>',
'<td><div>1月</div></td>',
'<td><div>2月</div></td>',
'<td><div>3月</div></td>',
'<td><div>4月</div></td>',
'</tr>',
'<tr>',
'<td><div>5月</div></td>',
'<td><div>6月</div></td>',
'<td><div>7月</div></td>',
'<td><div>8月</div></td>',
'</tr>',
'<tr>',
'<td><div>9月</div></td>',
'<td><div>10月</div></td>',
'<td><div>11月</div></td>',
'<td><div>12月</div></td>',
'</tr>',
'</tbody>',
'</table>',
'</div>',
'<div class="tips calendarPlugin" id="datepickerYears" style="display: none;">',
'<div class="calendarPlugin_top">',
'<a href="javascript:void(0);" class="i-calendarLeft" id="prevRangeYear"></a>',
'<span class="calendarPlugin_timeS" id="yearRangeDisplay">','</span>',
'<a href="javascript:void(0);" class="i-calendarRight" id="nextRangeYear"></a>',
'</div>',
'<table class="calendarPluginTable_other">',
'<tbody id="yearSelectorTBody"></tbody>',
'</table>',
'</div>'].join(''),

date: ['<td class="{isSelectable}" title="农历{lunarDateStr}" date="{stringDate}">',
'<div class="{isToday}">',
'<span>{currentDay}</span>','<p>{lunarDate}</p>',
'</div>',
'</td>'
].join(''),

year: ['<td class="{isSelectableYear}" year="{stringYear}">',
'<div class="{isThisYear}">{stringYear}</div>',
'</td>'].join('')

},
monthNames: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
monthNumber: ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
minYear: 1901,
maxYear: 2049,

/**
* 初始化
* @param {Object} target 必选,需要绑定的元素对象,要求是jQuery对象
* @param {Date} date 可选,初始化的时间
* @param {String} dateFormat 可选,日期显示格式。如"yyyy-MM-dd"
* @param {Function} callback 可选,选择日期后的回调
* @param {String} direction 可选,日历选择器的展示位置。默认展示在元素target下方并左对齐
*/
initialize: function(options) {
var self = this;
//参数初始化
options || (options = {}); //容错

if (!options.container) { //必须的参数检测
throw new Error('[CalendarSelector] container is empty!');
}
this.target = $(options.container);
//date参数转换和保存
var date = options.date || new Date();

if (typeof date !== 'object') {
date = Tools.Date.parse(date);
}

this.date = date || new Date();

this.dateFormat = options.dateFormat || "yyyy-MM-dd";
this.callback = options.callback || function () {};
this.direction = options.direction || "Auto";
this.cid = Math.floor(Math.random() * 1000000000);
this.pickerArea = $(Tools.Text.format(this.template.main, {cid: this.cid}));

this.lastTargetTop = this.target.offset().top;
this.target.on('click', function(event) {
self.show();
});
},
/**
* 事件绑定
*/
initEvents: function() {

var self = this;

this.prevMonthBtn.on('click', function(event) {
self.addMonth(-1);
});

...

//选择某年
this.yearSelectorTbodyEl.on('click', 'td',function(event) {
var tmpYear = $(event.currentTarget).attr("year");
if (tmpYear > self.maxYear || tmpYear < self.minYear){
return;
} else {
self.selectYear(tmpYear);
}

});
//选择某月
this.monthTdBtn.on('click', function(event) {
self.selectMonth(event);
});
// 选择某天
this.dateSelectorTbodyEl.on('click', 'td',function(event) {
self.changeDate($(event.currentTarget).attr("date"));
});

//注册mouseover与mouseout事件
this.monthSelectorTbodyEl.on({
mouseover: function() {
var clsName = $(this).attr('class');
if (!!!clsName) {
$(this).addClass("onHoverDay");
}
},
mouseout: function() {
if (!$(this).hasClass('onDay')){
$(this).removeClass("onHoverDay");
}
}
},'td div');

this.dateSelectorTbodyEl.on({
mouseover: function() {
if (!$(this).hasClass('onDay') ){
$(this).addClass("onHoverDay");
}
},
mouseout: function() {
if (!$(this).hasClass('onDay')){
$(this).removeClass("onHoverDay");
}
}
},'td div');

this.yearSelectorTbodyEl.on({
mouseover: function() {
if (!$(this).hasClass('onDay') ){
$(this).addClass("onHoverDay");
}
},
mouseout: function() {
if (!$(this).hasClass('onDay')){
$(this).removeClass("onHoverDay");
}
}
},'td div');
}
// 更多代码见后文下载
}

以上只是大概给出设计的思路,即完全的MVC思想。抽离模板,渲染数据,最后是对DOM进行相关操作。这种做法使得代码层次结构清晰,而且便于维护。以下给出所有代码的下载地址,欢迎各路朋友拍砖。