原生JavaScript实现造日历轮子

在平常开发中,大多数都是在和框架打交道,长此以往便遗忘了原生JS的感受,我的感受中原生JS基础仍是很重要的,因此最近就利用了空余时间造一个轮子出来,虽然以个人水平造出来的轮子质量仍是不太可靠的,可是我以为用来练练手仍是不错的,哈哈!!html

So, Let's begin!git

github: github.com/Zero-jian/p…程序员

如下是日历的样子,是有点难看,讲究讲究,重点在于JS部分,嘻嘻!!!github

关于日历组件的实现思路

  • 设置默认参数
  • 检查节点参数是否传入,不然抛出错误
  • 动态建立显示本日星期几的横轴
  • 动态建立日历的日子
  • 最后添加一点dom动做就好

先来看看构造函数内容

constructor(options) {
        let defaluteOptions = {
            element: null, //这是节点
            startOfWeek: 1,
            strings: {
                week: n => {
                    let map = {
                        0: '周日',
                        1: '周一',
                        2: '周二',
                        3: '周三',
                        4: '周四',
                        5: '周五',
                        6: '周六',
                    }
                    return map[n];
                },
                templateDay: `<li class="currentMonth">
                    <span class="dayLabel">
                        <span class="day"></span>
                        <span class="unit">日</span>
                    </span>
                </li>`
            },
            days: {},
        }
        //赋值默认参数
        this.options = Object.assign({}, defaluteOptions, options);
        //轮番就调用函数动态建立dom
        this.checkOptions()._generateTime()._generateWeekDay()._generatePrevMonth();
复制代码

初始化建立Calendar类对象的时候设置数值,赋值默认参数以及调用方法来动态建立dom,相信小伙伴们看懂这段代码没压力。数组

该轮子我全程都是用ES6写的,毕竟程序员仍是要跟上潮流的!!bash

赋值参数后开始轮番调用函数,首先调用的是this.checkOptions()方法,检查节点是否存在app

checkOptions() {
        //若是节点不存在直接抛出错误
        if (!this.options.element) {
            throw new Error('element is request');
        }
        return this;
    }
复制代码

接下来就是获取当天的年月日

毕竟是日历,获取当前的年月日当参考仍是很重要的框架

_generateTime() {
        let data = new Date(); //时间
        let year = this.options.days.year = data.getFullYear(); //年份
        let month = this.options.days.month = data.getMonth() + 1; //月份
        let day = this.options.days.day = data.getDate(); //日子
        this.options.days.countDay = 0; //日历总日子数为7*6=42
        this.options.days.noMonth = data.getMonth() + 1; //不变的月份
        this.options.days.noYear = data.getFullYear(); //不变的年份
        return this;
    }
复制代码

建立星期横轴

_generateWeekDay() {
        let {
            startOfWeek,
            strings
        } = this.options;
        let calendar = document.querySelector('.calendar');
        let ol = dom.create(`<ol class="weekdays"></ol>`);
        calendar.appendChild(ol);
        let weekIndex = this.createArray(7, startOfWeek).map((day, i) => {
            let li = dom.create(`<li>${strings.week(i)}</li>`);
            //判断是否为今天
            ol.appendChild(li);
        });
        return this;
    }
复制代码

dom.create是封装好的方法,传入模板便可建立并返回回来dom

this.createArray()也是封装好的方法,本函数是建立一个长度为7的数组,为何长度为7?由于周一到周日的长度为7啊,而后开始使用map映射和遍从来建立节点并添加document.body里面!!!函数

唔唔唔,去到这里,星期横轴就建立好了,接下来是重点部分了,就是建立对于的星期的日子日历,其实只要掌握逻辑就行了,不过由于我是菜鸡,写的时候也有点掉坑,因此,哈哈,大家对我写的代码参考参考就行了!!

接下来是重点了,就是建立日子

建立日历日子分为三个部分,第一部分是上个月的日子,第二是本月的日子,第三部分是下个月的日子,三个部分因此把它们分别封装起来,嫑相互影响!!

话很少说,贴上代码

//建立上个月日子
   _generatePrevMonth() {
        let date = this.options.days;
        let year = date.year;
        let month = date.month;
        let beginWeek = this._getWeekWeek(year, month - 1, 1); //开始星期
        let countMonth = this._getMonth(year, month - 1); //上月月份天数

        let calendar = document.querySelector('.calendar');
        let ol = dom.create(`<ol class="days"></ol>`);
        let li = document.querySelectorAll('.dayLabel>.day');
        calendar.appendChild(ol);

        date.countDay = 0;
        beginWeek == 0 ? beginWeek += 7 : ''; //若是月份开头为星期日,会出bug,这是防止
        date.countDay += beginWeek;
        this.createArray(42, this.options.startOfWeek).map((day, i) => {
            let li = dom.create(this.options.strings.templateDay);
            let span = li.querySelector('.dayLabel>.day');
            if (i < beginWeek) {
                span.textContent = countMonth - beginWeek + 1 + i;
            }
            ol.appendChild(li);
        });

        document.querySelector('h1.date').appendChild(dom.create(`<p data-role="time">${date.year}-${date.month}-${date.day}</p>`));
        this._generateCurrentDay()._generateNextMonth();
    }
复制代码

建立日子从头至尾,首先是建立上月的日子,逻辑就是先建立一个长度为42的数组,由于6*7=42,数组下标为0至41,而后获取上月天数以及本月一号开始是星期几,这样就能知道上月日子占位,好比5月1号是星期三,那么上个月的日子就占了3位置,而后经过循环赋值,这样就建立了日历上月的天数!!

有种特殊状况就是1号是星期天,beginWeek为0,而上月天数占比须要为7,因此就须要+7,要否则会出现问题

而后是建立本月的天数

按照惯例,贴上代码

//建立当前月份日子
_generateCurrentDay() {
        let date = this.options.days;
        let getWeek = this._getWeekWeek(date.year, date.month - 1, date.day); //星期几
        let getMonth = this._getMonth(date.year, date.month) //月份天数
        let getMonthDay = this._getWeekDay(); //几号
        let li = document.querySelectorAll('.dayLabel>.day');
        //建立当月日子模块
        let dayIndex = this.createArray(getMonth, this.options.startOfWeek).map((day, i) => {
            //判断日历起止
            i += date.countDay;
            li[i].textContent = i - date.countDay + 1;
            //判断是否为今天
            if (i == (getMonthDay + date.countDay - 1) && date.noMonth == date.month && date.noYear == date.year) {
                li[i].parentNode.classList.add('today');
            }
        });
        date.countDay += getMonth;

        return this;

    }
复制代码

建立当前月份日子,首先是知道本月月份的天数getMonth,而后建立长度为getMonth的数组,本月开始的下标为(0+data.countDay),而后经过循环赋值,顺便判断是否为今天,若是是今天就添加today的class,唔唔唔,大概就是如此~~~

最后就是下一个月的天数

代码 代码 代码

//建立下个月日子
_generateNextMonth() {
        let date = this.options.days;
        let year = date.year;
        let month = date.month;
        let beginWeek = this._getWeekWeek(year,month,1);//开始星期
        let countMonth = this._getMonth(year,month+1);//下月月份天数
        let li = document.querySelectorAll('.dayLabel>.day');
        //data.countDay统计了上月和本月的日子数总量,直接减去便可
        this.createArray(42-date.countDay , this.options.startOfWeek).map((day,i)=>{
            li[date.countDay+i].textContent = i+1;
        });
    }
复制代码

这个逻辑比较简单,就是用(6*7=42)42减去上月天数和本月天数,剩下的位置为显示下个月的天数,因此就是这样子!!!

把封装好的代码也弄出来吧~~

//dom.create()调用
let dom = {
    create(html) {
        let template = document.createElement('template');
        template.innerHTML = html;
        return template.content.firstChild;
    }
}
复制代码
//this.createArray()调用
    //建立数组节点
    createArray(length, fill) {
        let array = Array.apply(null, {
            length: length
        }).map(() => fill);
        return array;
    }
    
    //获取月份天数
    _getMonth(year, month) {
        return new Date(year, month, 0).getDate();
    }

    //获取星期几
    _getWeekWeek(year, month, day) {
        return new Date(year, month, day).getDay();
    }

    //获取当前月份日子
    _getWeekDay() {
        return new Date().getDate();
    }
复制代码

动做切换部分

切换日子这里相对来讲就是比较简单,我直接贴代码,大家一看就懂了

//上一个月
    previousMonth() {
        // this.options.days.month -= 1;
        this.changeMonth('prev');
    }

    //下一个月
    nextMonth() {
        // this.options.days.month += 1;
        this.changeMonth('next');
    }

    //回到今天
    resetMonth() {
        // this._generateTime();
        this.changeMonth('defalut');
    }

    //封装月份dom
    changeMonth(status) {
        let date = this.options.days;
        switch(status) {
            case 'prev': {
                --date.month < 1 ?  date.year-- ? date.month = 12 : '' : '';
                break;
            }

            case 'next': {
                ++date.month > 12 ?  date.year++ ? date.month = 1 : '' : '';
                break;
            }

            case 'defalut': {
                this._generateTime();
                break;
            }
        }
        //移除节点
        this._generateCalendar();
        //从新添加节点
        this._generatePrevMonth();
    }
复制代码

调用方式

let calendar = new Calendar({
        element: document.querySelector('.calendar')
    })
    //切换上月
    previousMonth.onclick = function () {
        calendar.previousMonth()
    }
    //切换下月
    nextMonth.onclick = function () {
        calendar.nextMonth()
    }
    //回到今月
    today.onclick = function () {
        calendar.resetMonth()
    }
复制代码

整个日历组件都是经过this.options.days.monththis.options.days.year这两参数来生成相对于的日历数据,因此你想要切换月份只需修改这两个参数便可!!!

整个逻辑思路都是本身想的,因此可能并不算得上是个很好的想法,因此仅供参考!!!哈哈,来自小白的不自信。。。

唔唔唔,整个日历组件下来大概就是这样子,整个流程写下来感受本身的思惟仍是有所进步的,可是其实我以为这个轮子代码仍是能够再封装封装和完善的,嘻嘻~~

轮子功能比较简单,因此剩下的功能就等待小伙伴们自由发挥了~~

好了,第一次写文章,熬夜写的,忽然就有灵感了,不愿睡觉,呵呵,,明天上班确定是要打瞌睡了,呵呵~~~

本人是小白,从业将近一年,因此代码上有什么错误,请各位大神可以指出指出,嗯嗯,完~~

相关文章
相关标签/搜索