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

前言

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

项目演示

本文项目地址

https://github.com/ruichengpi...html

本文演示地址

https://ruichengping.github.i...git

本文演示效果图

图片描述

--------------------------------分割线----------------------------------github

完整项目地址(移动端)

github地址

https://github.com/ruichengpi...数组

演示地址

https://ruichengping.github.i...浏览器

理一下思路

  基于上面的效果演示图,咱们第一件事就是理清思路。不少同窗呢,一开始若是作这个东西可能一头雾水,都不知道从哪里开始入手。这里我就你们理一下。dom

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

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

JS交互实现

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

  • 年份、月份的增长减小按钮

  • 根据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:若是以为本文写的不错,请记得点赞哦!)