前言: 最近几天都在研究日历控件编写,当然前提我要说明下,当然看过别人写的源码 所以脑子一热 就想用自己的编码方式 来写一套可扩展性 可维护性 性能高点的代码控件出来,就算练习练习下,所以前几天晚上下班后一直在研究 及 今天早上6点跑起来敲代码(昨天晚上加班回来到凌晨1点睡觉),就是不相信写不出来,所以脑子一热 就睡不觉 一直到现在 总算有点眉目,所以在此给大家分享下编写方式,代码不是最重要的 关键是要实现一个什么样的业务逻辑,想写一个什么样的出来,还有就是一刚开始 "设计" 非常重要, 个人觉得:一个良好的设计 是提高产品或者说代码可维护性 可扩展性 性能高的一个重要因素。设计不仅仅是"视觉设计师"的事情,设计对于我们前端开发或者后台开发也是一个非常重要的事情。一直以来会有很多后台开发人员 认为我们前端属于uI界面的工程师 或者说 我们只会HTML+CSS等 一说到程序员貌似就是指他们后台开发人员,其实我们前端也属于程序员啊,算了 不扯远了!

如何来设计?

  一说到 ”设计“ 这个词 我就想说说我最近做的一个项目,当然这个项目我只做了30%-40% 之前我在另外一家公司 后来就来这家公司做项目,我来后 这个项目已经完成了60%-70% 所以也是抱着一个完成任务的心情来完成任务,首先来说说之前没有很好的设计一些缺陷:

1. css不分离所有的css写在一个文件夹里面 并且每个页面命名方式都是一样 比如a页面 和 b页面 有一部分是一样的 所以结构也一样 但是某一天需求方说 b页面 那块我想改下 加一个按钮或者说 要美化下 a页面不动  好了 我们就接着在那个css文件里面改 改完后 发现b页面效果是达到了 但是a页面样式变了 不是我们想要的 所以我们接着又改 干脆在b页面上直接写内联样式(为了完成需求),但是这样有个缺点 以后不好维护。所以建议 各个页面的css分离。

2. 公共部分如何来重用? 比如说a页面 b页面 c页面 甚至所有的页面有一块很相似的地方 或者说功能也很相似 那么按道理 来说我们可以把它们提取出来作为公用的部分,后台开发人员也可以写一个VM 在每个页面需要的地方嵌套进去,对于以后我们维护也很方便 要改只改一个地方 全站生效!按道理来说 这也是一个比较好的设计,但是需求方又提要求了,他说我感觉a页面不好看 b页面也不好看 我想在a b页面公用的那部分增加有一些条件 或者 减少一部分 那么对于后台开发人员VM就不能公用了 我们css文件也不好改或者不能公用了 或者以后又说c页面我感觉也不怎么好看 我又想改 改来改去 现在开发人员可能纳闷了 算了你不是要经常改吗?我一开始时候 我每个页面写一个VM 不管他是公用不公用,这样的设计当然很不好,如果a页面 和 b页面 或者说全站页面 某部分也有相似的地方 那么以后要维护 那就要维护全站的页面 并且很容易出错!对于需求方老是改来改去的需求 我在想一个这么一个问题,我怎么样能快速开发?怎么样能设计一个项目?或者说怎么样去架构一个项目?改来改去 最后不管对于前端也好 后台也好 性能肯定不好 那么我们也只有怀着这样一个心情 只要能完成项目就行 只要需求方喜欢的就ok 性能嘛 反正慢点就慢点吧 需求方也不懂的!就好比我们要做2层房子一样 一刚开始设计师都设计好了 什么地方要该怎么做,比如一刚开始 客厅设计一个50平方米 当房子架子搭好了后 突然需求方说客厅要改成100平方米?你说可能吗 难道都把他们拆了 重新做? 我只想说:所有的页面设计师都设计好后 需求方应该要花充足的时间去分析 去看 那些不合理尽量早改 一旦设计搞定型了 那就不允许更改!你这么一改 改来改去 没有毛病的也会改来毛病的!

3.怎么样有个良好的设计?怎么样能架构一个良好的项目?我最近一直在思考这样的问题,如果一个良好的设计 不能适应需求方老是更改需求 属于一个良好的设计吗?

算了 上面的问题先讨论到这!如果大家有什么想法也可以一起分享出来,下面来看看我写的日历控件吧!

有以下优点:

1. 支持IE6+ 火狐 谷歌游览器等等。

2. 支持单日历 双日历 甚至多日历面板,暂显示输入框的日期只做了单日历和双日历的处理操作。考虑目前基本上用到最多的就是这2种。

3. 支持当前日期 之前的日期不可选择 不可操作。

4. 给输入框传了当前的值保存在value中 方便开发人员获取。

缺点:

1. 不支持多国语言 只支持中国的。

下面来看看单日历的效果图:

双日历效果图如下:

一: 看看可配置的参数及提供回调函数:

this.config = {
elemCls : '.input', // 目标元素
beginYear : 1990, //开始日期
endYear : 2020, //结束日期
panelCls : '.calendarPanel', // 日历面板类
bg_cur_day : 'bg_cur_day', // 当前的颜色
bg_out : 'bg_out', // 鼠标hover颜色
bg_over : 'bg_over', // 鼠标out颜色
date2StringPattern : 'yyyy-MM-dd', // 默认显示格式 yyyy-MM-dd
patternDelimiter : '-', // 分隔符 注意:分隔符要和上面显示格式对应
panelCount : 2, // 面板的个数 是单日历 双日历 或者 多日历等
manyDisabled : false, // 默认情况下为false 如果为true 指当前日期之前的日期不可点击
ishasSelect : true, // 是否有下拉框选择年份和月份 默认为true 暂不做操作
// 为以后留接口 因为如果没有的话 年月份没有显示出来 感觉怪怪的 render : null, // 渲染日历后触发
clickDayCallBack : null, // 点击某一天后 回调函数
clickPrevCallBack : null, // 点击上一月的回调函数
clickNextCallBack : null, // 点击下一页的回调函数
changeYearCallBack : null, // 下拉框改变年份的回调函数
changeMonthCallBack : null // 下拉框改变月份的回调函数
};

1.可以配置开始日期和结束日期:也就是下拉框渲染时候 渲染从开始日期和结束日期渲染:如下代码判断:

// 渲染下拉框所有的年份
_renderYear: function() {
var self = this,
_config = self.config,
_cache = self.cache;
var html = '';
for(var i = _config.beginYear; i <= _config.endYear; i+=1) {
html += '<option value="'+i+'">'+(i + Calendar.model['year'])+'</option>';
}
$(".yearSelect").each(function(index,item){
$(item).html(html);
});
},
// 渲染下拉框所有月份
_renderMonth: function(){
var self = this,
_config = self.config,
_cache = self.cache;
var html = '';
for(var i = 0; i < 12; i++) {
html+= '<option value="'+i+'">'+Calendar.model['months'][i]+'</option>'
}
$('.monthSelect').each(function(index,item){
$(item).html(html);
});
},

2. 输入框默认显示格式为XXXX-XX-XX 也可以显示格式为XXXX/XX/XX 或者其他的都行 但是date2StringPattern配置项也和patternDelimiter配置项对应 也就是说 如果这个date2StringPattern格式为XXXX-XX-XX 那么patternDelimiter分隔符为 - 如果date2StringPattern格式为XXXX/XX/XX  那么patternDelimiter分隔符为 / 对应起来。

3. panelCount 指面板的个数 如果为2的话 那么我在渲染的时候 会生成2个面板 如果是1个的话 那么就生成一个面板

4. manyDisabled 参数默认情况下为false 当他为true时候 说明今天是几号 那么今天之前的日期都为不可点击 且上一月的按钮也为不可点击的。

5. ishasSelect 默认情况下为true 是指是否需要下拉框 建议一般情况下不用改 默认为true 因为如果不显示的话 那么我不知道是那年那月 因为设计面板的时候也没有设计好 如果设计面板时候设计好了的话 那么可以用此参数。

提供以下回调函数:

1. render 渲染日历后触发。

2. clickDayCallBack 点击面板上某一天后的回调函数

3. clickPrevCallBack 点击上一月按钮的回调函数。

4. clickNextCallBack 点击下一月按钮的回调函数。

5. changeYearCallBack 下拉框改变年份的回调函数。

6. changeMonthCallBack 下拉框改变月份的回调函数。

公有的方法:

show: 显示日历

hide : 隐藏日历

 Calendar.model = {
"year" : "\u5e74",
"months" : ["\u4e00\u6708","\u4e8c\u6708","\u4e09\u6708","\u56db\u6708","\u4e94\u6708","\u516d\u6708","\u4e03\u6708","\u516b\u6708","\u4e5d\u6708",
"\u5341\u6708","\u5341\u4e00\u6708","\u5341\u4e8c\u6708"],
"weeks" : ["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d"],
"string2DatePattern" : "ymd"
};

上面年 月 星期 都编码过了。

思路如下:

1.点击输入框时候 判断日历是否已经渲染过的 如果已经渲染的 只是隐藏了 那么我直接显示就可以 否则的话 执行函数 把日历面板全部渲染出来 如下代码:

init: function(options){
this.config = $.extend(this.config,options || {});
var self = this,
_config = self.config,
_cache = self.cache;
$(_config.elemCls).unbind('click');
$(_config.elemCls).bind('click',function(){
// 判断下 如果日历面板已经渲染出来后 就不再渲染
if($(_config.panelCls + ' .calendarDiv').length > 0) {
self.show();
}else {
self.show();
self._draw();
self._renderYear();
self._renderMonth();
self._changeSelect();
self._renderData();
} });
},

_draw 函数 就是负责把所有的星期标题  渲染出来 如下图:

_renderYear函数 是把下拉框的年份计算出来。如下代码:

// 渲染下拉框所有的年份
_renderYear: function() {
var self = this,
_config = self.config,
_cache = self.cache;
var html = '';
for(var i = _config.beginYear; i <= _config.endYear; i+=1) {
html += '<option value="'+i+'">'+(i + Calendar.model['year'])+'</option>';
}
$(".yearSelect").each(function(index,item){
$(item).html(html);
});
},

_renderMonth 函数 是渲染下拉框所有的月份 如下代码:

// 渲染下拉框所有月份
_renderMonth: function(){
var self = this,
_config = self.config,
_cache = self.cache;
var html = '';
for(var i = 0; i < 12; i++) {
html+= '<option value="'+i+'">'+Calendar.model['months'][i]+'</option>'
}
$('.monthSelect').each(function(index,item){
$(item).html(html);
});
},

_changeSelect函数 是负责重新渲染当前的下拉框的年份和月份 重新赋值下拉框的年份和月份  如下代码:

_changeSelect: function(targetParent,date){
var self = this;
var ys,
ms;
if(targetParent) {
ys = $('.yearSelect',targetParent)[0];
ms = $('.monthSelect',targetParent)[0];
renderSelectYearVal(ys,targetParent);
renderSelectMonthVal(ms,targetParent);
}else {
$(".js-calendarTable").each(function(index,item){
ys = $('.yearSelect',item)[0],
ms = $('.monthSelect',item)[0];
renderSelectYearVal(ys);
renderSelectMonthVal(ms);
});
}
function renderSelectYearVal(ys,targetParent) { for(var i = 0; i < ys.length; i++) {
if(date) {
if(ys.options[i].value == date.getFullYear()){
ys[i].selected = true; // 重新获取当选被选中的年份 给页面隐藏域输入框重新赋值
var year = $(ys[i]).attr("value");
$('.js_year',targetParent).attr("year",year);
break;
}
}else {
if(ys.options[i].value == self.date.getFullYear()){
ys[i].selected = true;
break;
}
} }
}
function renderSelectMonthVal(ms,targetParent){ for(var i = 0; i < ms.length; i++) {
if(date) {
if(ms.options[i].value == date.getMonth()){
ms[i].selected = true;
// 重新获取当选被选中的年份 给页面隐藏域输入框重新赋值
var month = $(ms[i]).attr("value");
$('.js_year',targetParent).attr("month",month);
break;
}
}else {
if(ms.options[i].value == self.date.getMonth()){
ms[i].selected = true;
break;
}
}
}
}

_renderData 函数  是负责把几号渲染出来。
如下图

提供掩藏域

/*
* 一开始克隆当前年份和月份 保存到隐藏域去 目的当点击上下按钮时候 互不影响各自的年份 和月份
*/
self._year = self.cloneObject(self.year),
self._month = self.cloneObject(self.month);
$(".calendarDiv .js_year").attr({"year":self._year,"month":self._month});

所有的代码如下:

css代码:

<style>
* {margin:0;padding:0;}
body {font-family: "微软雅黑", Tahoma, Verdana;font-size: 12px;font-weight: normal;margin: 50px 10px;}
span, label, p, input {
font-family: "微软雅黑", Tahoma, Verdana;
font-size: 12px;
font-weight: normal;
}
form {
margin: 0;
padding: 0;
}
ul {
margin: 0;
}
h1 {
font-family: "微软雅黑", Tahoma, Verdana;
font-size: 16px;
font-weight: bold;
}
h2 {
font-family: "微软雅黑", Tahoma, Verdana;
font-size: 14px;
font-weight: bold;
}
div.effect {
border: 1px dotted #3C78B5;
background-color: #D8E4F1;
padding: 10px;
width: 98%;
}
div.source {
border: 1px solid #CCC;/*#090*/
background-color: #F5F5F5;/*#DFD*/
padding: 10px;
width: 98%;
}
.color_red {
color:#FF0000;
}
.b {
font-weight: bold;
}
select {font-size:12px;background-color:#EFEFEF;}
table {border:0px solid #CCCCCC;background-color:#FFFFFF}
th {font-size:12px;font-weight:normal;background-color:#FFFFFF;}
th.theader {font-weight:normal;background-color:#666666;color:#FFFFFF;width:24px;}
select.year {width:64px;}
select.month {width:60px;}
td {font-size:12px;text-align:center;cursor:pointer;}
td.sat {color:#0000FF;background-color:#EFEFEF;}
td.sun {color:#FF0000;background-color:#EFEFEF;}
td.normal {background-color:#EFEFEF;}
input.l,input.r,input.b {border: 1px solid #CCCCCC;background-color:#EFEFEF;width:20px;height:20px;cursor:pointer;}
input.b {width:100%;}
.calendarPanel .bg_out{background:#EFEFEF;}
.calendarPanel .bg_over{background:#FFCC00;}
.calendarPanel .bg_cur_day {background:#00CC33;}
.calendarPanel{
background-color: #FFFFFF;
z-index: 9999;
}
.calendarDiv {float:left;height: 216px;width: 200px;border: 1px solid #666666;}
.hidden{display:none;}
.calendarPanel .disabled{color:#DCDCDC;cursor:default;}
</style>

HTML代码:

<input type="text" class="input" style="width:200px;height:22px;"/>
<div class="calendarPanel hidden"> </div>

JS所有代码 页面相应的地方有注释

/**
* 日历控件编写 支持单日历 双日历 多日历等。
* @author tugenhua
* @time 2013 11-07
* @email 879083421@qq.com
*/ function Calendar() { this.config = {
elemCls : '.input', // 目标元素
beginYear : 1990, //开始日期
endYear : 2020, //结束日期
panelCls : '.calendarPanel', // 日历面板类
bg_cur_day : 'bg_cur_day', // 当前的颜色
bg_out : 'bg_out', // 鼠标hover颜色
bg_over : 'bg_over', // 鼠标out颜色
date2StringPattern : 'yyyy-MM-dd', // 默认显示格式 yyyy-MM-dd
patternDelimiter : '-', // 分隔符 注意:分隔符要和上面显示格式对应
panelCount : 2, // 面板的个数 是单日历 双日历 或者 多日历等
manyDisabled : false, // 默认情况下为false 如果为true 指当前日期之前的日期不可点击
ishasSelect : true, // 是否有下拉框选择年份和月份 默认为true 暂不做操作
// 为以后留接口 因为如果没有的话 年月份没有显示出来 感觉怪怪的 render : null, // 渲染日历后触发
clickDayCallBack : null, // 点击某一天后 回调函数
clickPrevCallBack : null, // 点击上一月的回调函数
clickNextCallBack : null, // 点击下一页的回调函数
changeYearCallBack : null, // 下拉框改变年份的回调函数
changeMonthCallBack : null // 下拉框改变月份的回调函数
}; this.cache = {
createPanelHTML : '',
flag : true,
year : '', //保存页面一渲染时候 当前的年份
month : '', //保存页面一渲染时候 当前的月份
storeDateArrs : []
};
this.date = new Date();
this.year = this.date.getFullYear();
this.month = this.date.getMonth(); }
Calendar.model = {
"year" : "\u5e74",
"months" : ["\u4e00\u6708","\u4e8c\u6708","\u4e09\u6708","\u56db\u6708","\u4e94\u6708","\u516d\u6708","\u4e03\u6708","\u516b\u6708","\u4e5d\u6708",
"\u5341\u6708","\u5341\u4e00\u6708","\u5341\u4e8c\u6708"],
"weeks" : ["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d"],
"string2DatePattern" : "ymd"
};
Calendar.prototype = { init: function(options){
this.config = $.extend(this.config,options || {});
var self = this,
_config = self.config,
_cache = self.cache;
$(_config.elemCls).unbind('click');
$(_config.elemCls).bind('click',function(){
// 判断下 如果日历面板已经渲染出来后 就不再渲染
if($(_config.panelCls + ' .calendarDiv').length > 0) {
self.show();
}else {
self.show();
self._draw();
self._renderYear();
self._renderMonth();
self._changeSelect();
return;
self._renderData();
} });
},
_draw: function(){
var self = this,
_config = self.config,
_cache = self.cache; // 拼接HTML结构
_cache.createPanelHTML += '<div class="calendarDiv">'+
'<table class="js-calendarTable" width="100%" border="0" cellpadding="3" cellspacing="1" align="center">' +
'<tr>' +
'<th><input class="l goPrevMonthButton" name="goPrevMonthButton" type="button" value="<" \/><\/th>' +
'<th colspan="5">'+
'<select class="year yearSelect" name="yearSelect"><\/select>'+
'<select class="month monthSelect" name="monthSelect"><\/select>'+
'<\/th>' +
'<th><input class="r goNextMonthButton" name="goNextMonthButton" type="button" value=">" \/><\/th>' +
'<\/tr>';
_cache.createPanelHTML += '<tr>';
for(var i = 0; i < 7; i++) {
_cache.createPanelHTML += '<th class="theader">'+
Calendar.model["weeks"][i] +
'<\/th>';
}
_cache.createPanelHTML += '<\/tr>'; for(var k = 0; k < 6; k+=1) {
_cache.createPanelHTML += '<tr align="center">';
for(var j = 0; j < 7; j++) {
switch (j) {
case 0: _cache.createPanelHTML += '<td class="sun"> <\/td>'; break;
case 6: _cache.createPanelHTML += '<td class="sat"> <\/td>'; break;
default: _cache.createPanelHTML += '<td class="normal"> <\/td>'; break;
}
}
_cache.createPanelHTML += '<\/tr>';
} _cache.createPanelHTML += '<tr>' +
'<th colspan="2"><input type="button" class="b clearButton" name="clearButton" value="清空"\/><\/th>'+
'<th colspan="3"><\/th>' +
'<th colspan="2"><input type="button" class="b closeButton" name="closeButton" value="关闭"\/><\/th>' +
'<\/tr>' +
'<input type="hidden" class="js_year" year="" month=""/>' +
'<\/table>' +
'<\/div>';
for(var m = 0; m < _config.panelCount; m+=1) {
if(_cache.flag) { $(_config.panelCls).append(_cache.createPanelHTML);
/*
* 一开始克隆当前年份和月份 保存到隐藏域去 目的当点击上下按钮时候 互不影响各自的年份 和月份
*/
self._year = self.cloneObject(self.year),
self._month = self.cloneObject(self.month);
$(".calendarDiv .js_year").attr({"year":self._year,"month":self._month});
}
}
if(!_config.ishasSelect) {
!$(".yearSelect").hasClass("hidden") && $(".yearSelect").addClass("hidden");
!$(".monthSelect").hasClass("hidden") && $(".monthSelect").addClass("hidden");
}
_cache.year = self.year;
_cache.month = self.month;
_cache.flag = false; _config.render && $.isFunction(_config.render) && _config.render(); $(".goPrevMonthButton").unbind('click');
$(".goPrevMonthButton").bind('click',function(e){
self._goPrevMonth(e);
_config.clickPrevCallBack && $.isFunction(_config.clickPrevCallBack) && _config.clickPrevCallBack();
});
$(".goNextMonthButton").unbind('click');
$(".goNextMonthButton").bind("click",function(e){
self._goNextMonth(e);
_config.clickNextCallBack && $.isFunction(_config.clickNextCallBack) && _config.clickNextCallBack();
});
$(".yearSelect").change(function(e){
self._update(e);
_config.changeYearCallBack && $.isFunction(_config.changeYearCallBack) && _config.changeYearCallBack();
}); $(".monthSelect").change(function(e){
self._update(e);
_config.changeMonthCallBack && $.isFunction(_config.changeMonthCallBack) && _config.changeMonthCallBack();
});
$(".clearButton").unbind('click');
$(".clearButton").bind('click',function(e){
$(_config.elemCls).val('');
$(_config.elemCls).attr('value','');
_cache.storeDateArrs[0] = undefined;
_cache.storeDateArrs[1] = undefined;
}); $(".closeButton").unbind('click');
$(".closeButton").bind('click',function(){
self.hide();
});
},
// 渲染下拉框所有的年份
_renderYear: function() {
var self = this,
_config = self.config,
_cache = self.cache;
var html = '';
for(var i = _config.beginYear; i <= _config.endYear; i+=1) {
html += '<option value="'+i+'">'+(i + Calendar.model['year'])+'</option>';
}
$(".yearSelect").each(function(index,item){
$(item).html(html);
});
},
// 渲染下拉框所有月份
_renderMonth: function(){
var self = this,
_config = self.config,
_cache = self.cache;
var html = '';
for(var i = 0; i < 12; i++) {
html+= '<option value="'+i+'">'+Calendar.model['months'][i]+'</option>'
}
$('.monthSelect').each(function(index,item){
$(item).html(html);
});
},
_renderData: function(targetParent,date) {
var self = this,
_config = self.config,
_cache = self.cache; var dateArray,
tds;
if(targetParent) {
tds = $("td",$(targetParent));
dateArray = self._getMonthViewDateArray(date.getFullYear(),date.getMonth());
renderTDs(tds,dateArray,date);
}else {
$(".js-calendarTable").each(function(index,item){
tds = $('td',item);
dateArray = self._getMonthViewDateArray(self.date.getFullYear(),self.date.getMonth());
renderTDs(tds,dateArray);
});
}
function renderTDs(tds,dateArray,date){
$(tds).each(function(index,td){
$(td).hasClass(_config.bg_cur_day) && $(td).removeClass(_config.bg_cur_day);
});
for(var i = 0; i < tds.length; i+=1) {
!$(tds[i]).hasClass(_config.bg_out) && $(tds[i]).addClass(_config.bg_out);
$(tds[i]).html("");
$(tds[i]).html(dateArray[i]) || " ";
if (i > dateArray.length - 1) continue;
if(dateArray[i]) {
$(tds[i]).unbind('click');
$(tds[i]).bind('click',function(e){
var target = e.target,
tagParent = $(target).closest('.js-calendarTable');
var year = $(".js_year",tagParent).attr("year"),
month = $(".js_year",tagParent).attr("month"); var curdate = new Date(year,month,1); if($(_config.elemCls)) {
if($(this).hasClass("disabled")) {
return;
}
// 暂时只考虑2种情况 单日历面板 和 双日历面板 输入框显示问题
if(_config.panelCount == 2) {
var parent = $(this).closest('.js-calendarTable'),
curIndex = $(".js-calendarTable").index(parent); if(curIndex == 0) {
_cache.storeDateArrs[0] = new Date(curdate.getFullYear(),curdate.getMonth(),$(this).html())._format(_config.date2StringPattern);
}else {
_cache.storeDateArrs[1] = new Date(curdate.getFullYear(),curdate.getMonth(),$(this).html())._format(_config.date2StringPattern);
}
if(_cache.storeDateArrs[0] != undefined && _cache.storeDateArrs[1] == undefined){ //先去掉类 bg_cur_day
$('td',tagParent).each(function(index,td){
$(td).hasClass(_config.bg_cur_day) && $(td).removeClass(_config.bg_cur_day);
});
$(_config.elemCls).val(_cache.storeDateArrs[0]);
$(_config.elemCls).attr('value',_cache.storeDateArrs[0]);
!$(this).hasClass(_config.bg_cur_day) && $(this).addClass(_config.bg_cur_day); }else if(_cache.storeDateArrs[0] == undefined && _cache.storeDateArrs[1] != undefined){
//先去掉类 bg_cur_day
$('td',tagParent).each(function(index,td){
$(td).hasClass(_config.bg_cur_day) && $(td).removeClass(_config.bg_cur_day);
});
$(_config.elemCls).val(_cache.storeDateArrs[1]);
$(_config.elemCls).attr('value',_cache.storeDateArrs[1]);
!$(this).hasClass(_config.bg_cur_day) && $(this).addClass(_config.bg_cur_day); }else if(_cache.storeDateArrs[0] != undefined && _cache.storeDateArrs[1] != undefined) { if(Date.parse(_cache.storeDateArrs[0]) >= Date.parse(_cache.storeDateArrs[1])) {
alert("结束日期必须大于开始日期 或者 开始日期必须小于结束日期");
return;
}else {
$(_config.elemCls).val(_cache.storeDateArrs[0] + '/' + _cache.storeDateArrs[1]);
$(_config.elemCls).attr("value",_cache.storeDateArrs[0] + '/' + _cache.storeDateArrs[1]);
self.hide();
}
}
}else{
$(_config.elemCls).val(new Date(curdate.getFullYear(),curdate.getMonth(),$(this).html())._format(_config.date2StringPattern));
self.hide();
}
}
_config.clickDayCallBack && $.isFunction(_config.clickDayCallBack) && _config.clickDayCallBack();
}); $(tds[i]).hover(function(){
if($(this).hasClass("disabled")){
return;
}
!$(this).hasClass(_config.bg_over) && $(this).addClass(_config.bg_over); },function(){
$(this).hasClass(_config.bg_over) && $(this).removeClass(_config.bg_over);
}); var today = new Date();
if(today.getFullYear() == self.date.getFullYear() && today.getMonth() == self.date.getMonth()) {
if(today.getDate() == dateArray[i]){
// 获取当前i 第几项 循环下 当前的i 前面所有的单元格不可点击
if(_config.manyDisabled){
self._cellDisabled(tds,i);
}
!$(tds[i]).hasClass(_config.bg_cur_day) && $(tds[i]).addClass(_config.bg_cur_day);
}
}else { $(tds[i]).hasClass(_config.bg_cur_day) && $(tds[i]).removeClass(_config.bg_cur_day);
}
}
}
}
},
_cellDisabled: function(tds,i){
for(var k = 0; k < i; k++) {
!$(tds[k]).hasClass("disabled") && $(tds[k]).addClass("disabled");
}
},
// 上一页按钮
_goPrevMonth: function(e){
var self = this,
_config = self.config,
_cache = self.cache;
var target = e.target,
targetParent = $(target).closest('.js-calendarTable');
var year = $(".js_year",targetParent).attr('year'),
month = $(".js_year",targetParent).attr('month'); if(_config.manyDisabled) {
return;
}
if(year == _config.beginYear && month == 0) {
return;
}
month--;
if(month < 0) {
year--;
month = 11;
}
var date = new Date(year,month,1);
self._changeSelect(targetParent,date);
self._renderData(targetParent,date); // 重新渲染td背景色
self._renderTdBg(year,month,targetParent);
},
// 下一页按钮
_goNextMonth: function(e){
var self = this,
_config = self.config,
_cache = self.cache;
var target = e.target,
targetParent = $(target).closest('.js-calendarTable'); var year = $(".js_year",targetParent).attr('year'),
month = $(".js_year",targetParent).attr('month');
if(year == _config.beginYear && month == 0) {
return;
}
month++;
if(month > 12) {
year++;
month = 0;
}
var date = new Date(year,month,1);
self._changeSelect(targetParent,date);
self._renderData(targetParent,date); // 重新渲染td背景色
self._renderTdBg(year,month,targetParent);
}, // 渲染当前天的背景色
_renderTdBg: function(year,month,targetParent){
var self = this,
_config = self.config,
_cache = self.cache; if(_cache.year == year && _cache.month == month) {
return;
}else {
var tds = $("td",targetParent); $.each(tds,function(index,td){
$(td).hasClass(_config.bg_cur_day) && $(td).removeClass(_config.bg_cur_day);
});
}
},
/**
* 深度克隆一个对象 使原对象和新对象完全独立
*/
cloneObject: function(obj){
if(obj === null){
return null;
}else if(obj instanceof Array){
var arr = [];
for(var i = 0, ilen = obj.length; i < ilen; i+=1){
arr[i] = obj[i];
}
return arr;
}else if(obj instanceof Date || obj instanceof RegExp || obj instanceof Function){
return obj;
}else if(obj instanceof Object){
var o = {};
for(var i in obj){
if(obj.hasOwnProperty(i)){
o[i] = cloneObject(obj[i]);
}
}
return o;
}else{
return obj;
}
},
show: function(){
var self = this,
_config = self.config;
$(_config.panelCls).hasClass('hidden') && $(_config.panelCls).removeClass('hidden');
},
hide: function(){
var self = this,
_config = self.config;
!$(_config.panelCls).hasClass('hidden') && $(_config.panelCls).addClass('hidden');
},
_getMonthViewDateArray: function(y,m) {
var dateArray = new Array(42); // 返回表示星期的第一天的数字
var dayOfFirstDate = new Date(y, m, 1).getDay(), // 返回月份的最后一天
dateCountOfMonth = new Date(y, m + 1, 0).getDate(); for(var i = 0; i < dateCountOfMonth; i+=1) {
dateArray[i + dayOfFirstDate] = i + 1;
}
return dateArray;
},
_update: function(e) {
var self = this,
target = e.target,
targetParent = $(target).closest('.js-calendarTable'),
monthSelect = $(".monthSelect",targetParent)[0],
yearSelect = $(".yearSelect",targetParent)[0]; self.year = yearSelect.options[yearSelect.selectedIndex].value;
self.month = monthSelect.options[monthSelect.selectedIndex].value;
self.date = new Date(self.year,self.month,1); self._changeSelect(targetParent,self.date);
self._renderData(targetParent,self.date);
},
// 重新渲染当前的下拉框的年份和月份 重新赋值下拉框的年份和月份
_changeSelect: function(targetParent,date){
var self = this;
var ys,
ms;
if(targetParent) {
ys = $('.yearSelect',targetParent)[0];
ms = $('.monthSelect',targetParent)[0];
renderSelectYearVal(ys,targetParent);
renderSelectMonthVal(ms,targetParent);
}else {
$(".js-calendarTable").each(function(index,item){
ys = $('.yearSelect',item)[0],
ms = $('.monthSelect',item)[0];
renderSelectYearVal(ys);
renderSelectMonthVal(ms);
});
}
function renderSelectYearVal(ys,targetParent) { for(var i = 0; i < ys.length; i++) {
if(date) {
if(ys.options[i].value == date.getFullYear()){
ys[i].selected = true; // 重新获取当选被选中的年份 给页面隐藏域输入框重新赋值
var year = $(ys[i]).attr("value");
$('.js_year',targetParent).attr("year",year);
break;
}
}else {
if(ys.options[i].value == self.date.getFullYear()){
ys[i].selected = true;
break;
}
} }
}
function renderSelectMonthVal(ms,targetParent){ for(var i = 0; i < ms.length; i++) {
if(date) {
if(ms.options[i].value == date.getMonth()){
ms[i].selected = true;
// 重新获取当选被选中的年份 给页面隐藏域输入框重新赋值
var month = $(ms[i]).attr("value");
$('.js_year',targetParent).attr("month",month);
break;
}
}else {
if(ms.options[i].value == self.date.getMonth()){
ms[i].selected = true;
break;
}
}
}
}
}
}; /*
* 日期格式化方法
*/
if(!Date.prototype._format) {
Date.prototype._format = function(str) {
var o = {
"M+" : this.getMonth() + 1, //month
"d+" : this.getDate(), //day
"h+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"w+" : "\u65e5\u4e00\u4e8c\u4e09\u56db\u4e94\u516d".charAt(this.getDay()), //week
"q+" : Math.floor((this.getMonth() + 3) / 3), //quarter
"S" : this.getMilliseconds() //millisecond
}
if (/(y+)/.test(str)) {
str = str.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for(var k in o){
if (new RegExp("("+ k +")").test(str)){
str = str.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return str;
}
}
/*
* 转换为日期
*/
if(!String.prototype._toDate) {
String.prototype._toDate = function(delimiter, pattern) {
delimiter = delimiter || "-";
pattern = pattern || "ymd";
var a = this.split(delimiter);
var y = parseInt(a[pattern.indexOf("y")], 10);
if(y.toString().length <= 2) y += 2000;
if(isNaN(y)) y = new Date().getFullYear();
var m = parseInt(a[pattern.indexOf("m")], 10) - 1;
var d = parseInt(a[pattern.indexOf("d")], 10);
if(isNaN(d)) d = 1;
return new Date(y, m, d);
};
} // 页面初始化方式
$(function(){
new Calendar().init({
//manyDisabled: true
//ishasSelect: true
});
});

代码也不好解释,口才不好,具体的可以看代码 实现相应的逻辑。先到这吧 写了一天了 明天还得继续加班 嗨!!

JS编写日历控件(支持单日历 双日历 甚至多日历等)的更多相关文章

  1. 简洁JS 日历控件 支持日期和月份选择

    原文出处 以下这个JS日历控件是我的闲暇之余自己编写的,所有的代码全部在IE7/IE8/Firefox下面测试通过, 而且可以解决被iframe层遮盖的问题.现在只提供两种风格(简洁版和古典版)和两种 ...

  2. 2.23 js处理日历控件(修改readonly属性)

    2.23 js处理日历控件(修改readonly属性) 前言    日历控件是web网站上经常会遇到的一个场景,有些输入框是可以直接输入日期的,有些不能,以我们经常抢票的12306网站为例,详细讲解如 ...

  3. JS日历控件优化(增加时分秒)

    JS日历控件优化      在今年7月份时候 写了一篇关于 "JS日历控件" 的文章 , 当时只支持 年月日 的日历控件,现在优化如下:      1. 在原基础上 支持 yyyy ...

  4. JS日历控件 灵活设置: 精确的时分秒.

     在今年7月份时候 写了一篇关于 "JS日历控件" 的文章 , 当时仅仅支持 年月日 的日历控件,如今优化例如以下:      1. 在原基础上 支持 yyyy-mm-dd 的年月 ...

  5. 日历控件修改的JS代码

    var bMoveable=true; var _VersionInfo=" " ; //============================================= ...

  6. JS实现日历控件选择后自动填充

    最近在做人事档案的项目,在做项目的初期对B/S这块不是很熟悉,感觉信心不是很强,随着和师哥同组人员的交流后发现,调试程序越来越好了,现在信心是倍增,只要自己自己踏实的去研究.理解代码慢慢的效果就出来了 ...

  7. 自己用js写的两个日历控件

    前一阵写了两个日历控件,做了简单的封装,发出来共朋友们参考. 第一个日历控件,条状的日历. (使用方法:调用initBarTime(id,evn),第一个参数是要渲染div的id,第二个参数是点击日期 ...

  8. js非常强大的日历控件fullcalendar.js, 日期时间库: moment.js

    日历控件: https://fullcalendar.io/docs/ https://fullcalendar.io/docs/event_data/events_function/ https:/ ...

  9. selenium+Python(Js处理日历控件)

    日历控件是web网站上经常会遇到的一个场景,有些输入框是可以直接输入日期的,有些不能,以我们经常抢票的12306网站为例,详细讲解如何解决日历控件为readonly属性的问题. 基本思路:先用js去掉 ...

随机推荐

  1. cf1102F. Elongated Matrix(状压dp)

    题意 题目链接 Sol \(n \leqslant 16\)可以想到状压 我们可以预处理出任意两行之间每列的最小值以及相邻两列的最小值 然后枚举一个起点,\(f[sta][i]\)表示走过了\(sta ...

  2. 【读书笔记】iOS-网络-使用Bonjour实现自组织网络

    Bonjour就是这样一种技术:设备可以通过它轻松探测并连接到相同网络中的其他设备,整个过程只需要很少的用户参与或是根本就不需要用户参与.该框架提供了众多适合于移动的使用场景,如基于网络的游戏,设备间 ...

  3. HTML/CSS学习(二)

    续...... ============================================================================================ ...

  4. 安装cuda8.0时无法安装.net Framework 4.0 错误的解决

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在win7 64位旗舰版(带sp1)上安装cuda时到安装Microsoft.NET Framework4.0,一直停 ...

  5. SD从零开始51-54 信用控制范围, 信用范围数据维护, 自动信用控制, 信用控制-阻止后续功能

    [原创] SD从零开始51 信用控制范围 分散的组织结构Decentralized Organization 信用控制范围是一个为客户指定和控制信用限额的组织单元: 依赖于你公司的需求,应收款可以使用 ...

  6. php 实现简单加入购物车(1)

          这个购物车相对来说比较简单,用于短暂存储,并没有存储到数据库,购物车对于爱网购的人来说简直是熟悉的不能再熟悉了,在写购物车之前,我们首先要构思一下,我们需要先从数据库中调出一张表格,这里我 ...

  7. Windows Win7建立wifi热点,手机共享WIFI上网

    Win7建立wifi热点,手机共享wifi上网 by:授客 QQ:1033553122 1.以管理员身份运行命令提示符:快捷键win+R→输入cmd→回车 2.启用并设定虚拟WiFi网卡:运行命令:n ...

  8. WiFi 统一管理以及设备自动化测试实践

    ATX 安卓设备 WiFi 统一管理以及设备自动化测试实践 (零散知识梳理总结) 此文为转载,感谢作者  目录  众所周知,安卓单台设备的UI自动化测试已经比较完善了,有数不清的自动化框架或者工具.但 ...

  9. vm virtualBox下 centos7 Linux系统 与本地 window 系统 网络连接 配置

    由于要模拟生产环境开发,所以要在自己的电脑上安装虚拟机,这里做一下记录. centos与本机网络连接 1. 环境 虚拟机 VirtualBox-5.2.0-118431-Win Linux镜像 Cen ...

  10. 【前端】NodeJs包管理工具NPM

    NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS部署上的很多问题. 测试是否安装成功,出现版本提示表示安装成功. npm -v NPM常用命令 官方文档:https://www.npm ...