您的位置:首页 > Web前端 > JavaScript

带你轻松搞定时间选择控件原理

2017-08-12 15:02 417 查看

前言

  说到这个时间选择控件,网上有很多各式各样的,相信很多同学们也都有用过,所以大家对这个也不陌生。虽然大家都用过这个时间选择控件,但是却很少有人去研究其中原理。最近本人利用闲暇时间自己写了一个时间选择控件,借这个时间选择控件向各位同学们阐述这个时间选择控件的原理。我向大家演示肯定是比较简单,相对来说更容易理解一点。但是呢,考虑到实用性,我就把这个时间选择控件改进了一下,让其变成了一个移动端时间选择控件,希望同学们如果喜欢我这边文章的话,麻烦帮个点个赞哦!不胜感谢!

项目演示

本文项目地址

https://github.com/ruichengping/Jcalendar/tree/master/learn

本文演示地址

https://ruichengping.github.io/Jcalendar/learn/

本文演示效果图



——————————–分割线———————————-

完整项目地址(移动端)

github地址

https://github.com/ruichengping/Jcalendar

演示地址

https://ruichengping.github.io/Jcalendar/demo/

理一下思路

  基于上面的效果演示图,我们第一件事就是理清思路。很多同学呢,一开始如果做这个东西可能一头雾水,都不知道从哪里开始入手。这里我就大家理一下。

  对这种插件的开发,我个人建议先不要急着考虑其封装之类的情况,我们就从最简单的开始入手。那什么是最简单的呢?那肯定就是html+css+js的模式最简单的。什么意思呢?我这里解释一下,现在就是单纯做一个效果,html控制骨架,css美化样式,js实现交互,不要考虑复用性。

  既然我们选择html+css+js先去实现效果,那么着手开始先把时间选择控件的html的dom结构写出来,然后用css去调整其中的样式。html和css的内容很简单,这里我就不把代码贴出来,同学们看完这篇文章可以去我的github项目下中Jcalendarlearn文件夹查看。下面我们说一下交互有哪些东西。

JS交互实现

  关于这个JS交互,我们首先要弄清楚这个时间选择控件有哪些交互。我先列举一下:

年份、月份的增加减少按钮

根据input框的位置,设置时间选择器的位置

根据年份、月份获取对应的日期数据

日期的选择

时间选择器显示隐藏

下面一一这些功能。

根据年份、月份获取对应的日期数据

  我们先来阐述这个功能是如何实现,这是整个时间选择控件的基石,弄清楚这个,后面的问题都会变得简单。初看到这个确实很难,一头雾水,都不知道如何下手。遇到这种问题的时候,先不要考虑如何去实现,首先我们弄清楚这个日期数据是由那几块组成。我认真地想想,发现每一组日期数据都有这样一个等式。

日期数据=上一月的日期+这个月的日期+下个月的日期

那为什么会这么组成呢?可能有同学们有这样子的疑问,不急不急,听我接下来解释一下。

一个星期是有七天,我们最长的一个月是31天,就是4个星期余三天。如果每一行代表星期,那么五行就可以搞定了。但是看效果演示图我们可以看出来,我们用了6行。为什么会多一行?这个问题很好解释,并不是每一个月的第一天都是从星期天开始的,所以我们要考虑最坏的情况。如果从星期六开始,此时需要6+31=37格(换上一下,就是5行多2格)。为了满足这种最坏的情况,我们就需要6行了。

通过上面解释,我们可以发现有一个特例并不满足这个等式。那就是这个月第一天是从星期天开始的。不过这个特例并不影响我们用这种思路去思考问题,所以我们可以不管这个特例。下面我就看一下,这每一块数据是如何得到的。

上个月

关于得出这块数据,我们先不要考虑其具体组成。首先考虑的是个数,需要几个我们给它弄几个。怎么知道需要多少个呢?很简单,弄清楚这个月的第一天是星期几就可以了。

/**
*获取currentYear年currentMonth月的第一天是星期几
*month参数是要比实际上少一天的
*0 代表星期天 6代表星期六
**/
new Date(currentYear,currentMonth-1,1).getDay();


当我得出需要几个上一月日期数据之后,我们还需要一样东西。那就是上一个月的最后一天,根据这个来往前推。

/**
*获取currentYear年currentMonth月的最后一天的日期
*month参数是要比实际上少一天的
**/
new Date(currentYear,currentMonth-1,0).getDate();


这个月

这个月的日期数据相比较上一个就简单多了,只需要知道这一月的第一天和最后一天即可。

//获取currentYear年currentMonth月的第一天的日期
new Date(currentYear,currentMonth-1,1).getDate();
//获取currentYear年currentMonth月的最后一天的日期
new Date(currentYear,currentMonth,0).getDate();


下一月

这块数据也非常简单,总共7*6=42格,剩下几格就往里面填几个。我们这里只需要知道下一个月的第一天是多少就行了。

//获取currentYear年currentMonth月的下一月的一天的日期
new Date(currentYear,currentMonth,1).getDate();


最终的JS代码

//根据年,月获取日数组
function getMonthData(year, month, day) {
var days = [];
var today = new Date();
if (!year | !month | !day) {
year = today.getFullYear();
month = today.getMonth() + 1;
day = today.getDate();
}
//获取该月第一天的Date对象
var firstDateObj = new Date(year, month - 1, 1);
//获取该月第一天对应的星期几
var firstDateWeekDay = firstDateObj.getDay();
//获取该月最后一天的Date对象
var lastDateObj = new Date(year, month, 0);
//获取该月最后一天的日期
var lastDate = lastDateObj.getDate();
//获取上一个月最后一天的Date对象
var lastDateOfPrevMonthObj = new Date(year, month - 1, 0);
//获取上一个月最后一天的日期
var lastDateOfPrevMonth = lastDateOfPrevMonthObj.getDate();
//上月
for (var i = 0; i < firstDateWeekDay; i++) {
var className = "available disabled";
var thisMonth = month - 1;
var date = lastDateOfPrevMonth - firstDateWeekDay + i + 1;
if (thisMonth === 0) {
thisMonth = 1;
}
days.push({
"date": date,
"showDate": date,
"thisMonth": thisMonth,
"className": className
});
}
//本月
for (var i = 0; i < lastDate; i++) {
var className = "available";
var date = i + 1;
var thisMonth = month;
if (date === day) {
className = "available current";
}
if (today.getDate() === date && today.getFullYear() === year && today.getMonth() + 1 === month) {
days.push({
"date": date,
"showDate": "今天",
"thisMonth": thisMonth,
"className": className
});
} else {
days.push({
"date": date,
"showDate": date,
"thisMonth": thisMonth,
"className": className
});
}

}
var nextMonthLength = days.length;
//下月
for (var i = 0; i < 7 * 6 - nextMonthLength; i++) {
var className = 'available disabled';
var date = i + 1;
var thisMonth = month + 1;
if (thisMonth === 13) {
thisMonth = 12;
}
days.push({
"date": date,
"showDate": date,
"thisMonth": thisMonth,
"className": className
});
}
return {
"year": firstDateObj.getFullYear(),
"month": firstDateObj.getMonth() + 1,
"days": days
}
}


年份、月份的增加减少按钮

  这块没有难的点,需要注意的就是临界值得判断。比如说12月再加1个月,不能变成13月,而是年份加1,月份置为1.

根据input框的位置,设置时间选择器的位置

  这块内容也很简单,弄清楚left值和top值是如何计算的即可。

top值=input输入框到浏览器窗口顶部的距离+input自身的高度

left值=input输入框到浏览器窗口左边的距离

  上面需要注意的是距离游览器而不是整个文档,因为我们用的fixed而不是absolute。

日期的选择

  这里没有难点,但是有一个新手非常容易犯错的错误。在为日期绑定事件的时候,新手很容易就会找到当前页面所有日期给它绑定事件。这样显然是行不通的,因为日期数据是不断变得,也就是日期这些dom元素是会替换了的,之前绑定的事件也就不见了,所以我建议大家用事件委托机制。可能会有人反驳我,每一天改变年份和月份的时候在重新绑定一次不就完了,当然这样也是可以的。但是不建议,简单的事情不要复杂化,无端增加开销。

时间选择器显示隐藏

  这个小功能点,很简单,没啥可讲。需要注意多个实例并且只有一个时间选择器的dom结构的情况下,你该如何设计你的显示隐藏。

结语

  到这里我们就把整个时间选择控件实现整个思路都理了一遍,相信同学们已经知道如何实现一个时间选择控件了。快去自己动手做一个自己专属的时间选择控件吧!(ps:如果觉得本文写的不错,请记得点赞哦!)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息