原生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這兩參數來生成相對於的日曆數據,因此你想要切換月份只需修改這兩個參數便可!!!

整個邏輯思路都是本身想的,因此可能並不算得上是個很好的想法,因此僅供參考!!!哈哈,來自小白的不自信。。。

唔唔唔,整個日曆組件下來大概就是這樣子,整個流程寫下來感受本身的思惟仍是有所進步的,可是其實我以爲這個輪子代碼仍是能夠再封裝封裝和完善的,嘻嘻~~

輪子功能比較簡單,因此剩下的功能就等待小夥伴們自由發揮了~~

好了,第一次寫文章,熬夜寫的,忽然就有靈感了,不願睡覺,呵呵,,明天上班確定是要打瞌睡了,呵呵~~~

本人是小白,從業將近一年,因此代碼上有什麼錯誤,請各位大神可以指出指出,嗯嗯,完~~

相關文章
相關標籤/搜索