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

前言

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

项目演示

本文项目地址

https://github.com/ruichengpi…

本文演示地址

https://ruichengping.github.i…

本文演示效果图

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

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

完整项目地址(移动端)

github地址

https://github.com/ruichengpi…

演示地址

https://ruichengping.github.i…

理一下思路

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

  对这种插件的开发,我个人建议先不要急着考虑其封装之类的情况,我们就从最简单的开始入手。那什么是最简单的呢?那肯定就是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:如果觉得本文写的不错,请记得点赞哦!)

    原文作者:wuming
    原文地址: https://segmentfault.com/a/1190000010623303
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞