成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

帶你開發(fā)一個(gè)日歷控件

shiina / 1901人閱讀

摘要:直接使用事件代理機(jī)制,將事件綁定在整個(gè)日歷的上即可,這樣事件只用在創(chuàng)建時(shí)初始化一次即可,簡(jiǎn)單高效省內(nèi)存。

首發(fā)我的博客 - https://blog.cdswyda.com/post/2017121010

日歷控件多的不勝枚舉,為什么我們還要再造一個(gè)輪子呢?

因?yàn)榇蠖鄶?shù)日歷控件都是用于選擇日期的,有種需求是要在日歷上展示各種各樣的內(nèi)容,這樣的日歷控件較少,而且試用下來并不滿意。

因此就再造一個(gè)輪子,現(xiàn)在帶你一起基于使用之前完成的組件機(jī)制來開發(fā)一個(gè)日歷控件。

需求

簡(jiǎn)單把需求整理如下:

月視圖

支持在日歷中每一天中插入任意的內(nèi)容

相關(guān)點(diǎn)擊事件

獲取日歷當(dāng)前視圖的開始和結(jié)束日期

獲取設(shè)置選中的日期

實(shí)現(xiàn)分析

首先我們拿系統(tǒng)中自帶的日歷觀察一下,看看日歷的特征到底是怎么樣的。

一個(gè)月中有 28 到 31 天不等,但是為了保證完整的結(jié)構(gòu),日歷中會(huì)有部分上一月和下一月的日期,總結(jié)下來,一個(gè)月中顯示的必定是整整6周的日期。

那么只要得到當(dāng)月的開始日期就可以繪制日歷了。

如何計(jì)算當(dāng)月日歷視圖中的開始日期呢? 前面已經(jīng)分析了,為了保證完整,它顯示了上一月的部分天數(shù),那么只用從當(dāng)月的1號(hào)開始往前推算就可以了。

開始日期 = 當(dāng)月1號(hào)的日期 - 當(dāng)月1號(hào)的星期
結(jié)束日期 = 開始日期 + 42天

這個(gè)問題搞清楚了,感覺實(shí)現(xiàn)這么一個(gè)日歷就沒什么大阻礙了,開始動(dòng)工吧!

必要結(jié)構(gòu)準(zhǔn)備

首先構(gòu)建如下所示的基本結(jié)構(gòu)

其中:

頭部左右為個(gè)性化區(qū)域,用于實(shí)際使用時(shí)放置任意內(nèi)容。中間用于顯示當(dāng)前月份和切換按鈕

主體區(qū)域中用繪制整個(gè)日歷

thead 中繪制周一至周日 或周日至周一的星期,這段內(nèi)容是不會(huì)隨月份切換而改變的,可以直接準(zhǔn)備好

tbody 中用于繪制可變的日期,準(zhǔn)備好容器留空即可。

腳部區(qū)域用于實(shí)際使用時(shí)放置任意各項(xiàng)化內(nèi)容

menu區(qū)域用于切換日期時(shí)彈出的面板

繪制日歷

在初始化好日歷結(jié)構(gòu)后就可以開始繪制日歷了。

計(jì)算一個(gè)月中的開始日期和結(jié)束日期

首先完成開始和結(jié)束時(shí)間的計(jì)算

{
    // 初始化當(dāng)前月份的開始日期和結(jié)束日期
    _initStartEnd: function () {
        // 當(dāng)月1號(hào)
        var currMonth = moment(this.currMonth, "YYYY-MM"),
            // 當(dāng)月1號(hào)是周幾 the ISO day of the week with 1 being Monday and 7 being Sunday.
            firstDay_weekday = currMonth.isoWeekday(),
            startDateOfMonth,
            endDateOfMonth;
        if (!this.dayStartFromSunday) {
            // 開始為周一 則向前減少周幾的天數(shù)-1即為 開始的日期
            startDateOfMonth = currMonth.subtract(firstDay_weekday - 1, "day");
        } else {
            // 開始為周日 則直接向前周幾的天數(shù)即可
            startDateOfMonth = currMonth.subtract(firstDay_weekday, "day");
        }

        endDateOfMonth = startDateOfMonth.clone().add(41, "day");

        this.startDateOfMonth = startDateOfMonth;
        this.endDateOfMonth = endDateOfMonth;
    }
}

由于要處理很多日期,而JavaScript中關(guān)于日期處理時(shí),不同瀏覽器下差異較大,因此直接使用 moment.js 來對(duì)日期進(jìn)行統(tǒng)一處理。

由于使用習(xí)慣不同,一周的開始到底是周一還是周日是不確定的,因此直接作為配置即可。

繪制一月中的日期

上面已經(jīng)計(jì)算得到了一個(gè)月的開始日期和結(jié)束日期,那么只用遍歷進(jìn)行繪制即可。

由于我們使用了表格實(shí)現(xiàn),因此需要按行繪制。

實(shí)現(xiàn)如下:

{
    // 日歷可變部分的渲染
    _render: function () {
        this._initStartEnd();

        var weeks = 6,
            days = 7,
            curDate = this.startDateOfMonth.clone(),
            tr;

        var start = this.startDateOfMonth.format("YYYY-MM-DD"),
            end = this.endDateOfMonth.format("YYYY-MM-DD");

        // 清空 并開始新的渲染
        this._clearDays();
        this._renderTitle();

        for (var i = 0; i < weeks; ++i) {
            tr = document.createElement("tr");
            tr.className = "ep-calendar-week";
            this._daysBody.appendChild(tr);

            for (var j = 0; j < days; ++j) {
                // 渲染一天 并遞增
                this._renderDay(curDate, tr);
                curDate.add(1, "day");
            }
        }
    },
    // 每天的渲染
    _renderDay: function (date, currTr) {
        var td = document.createElement("td"),
            tdInner = document.createElement("div"),
            text = document.createElement("span"),
            day = date.isoWeekday(),
            // 返回的月份是0-11
            month = date.month() + 1;

        tdInner.appendChild(text);
        td.appendChild(tdInner);

        td.className = "ep-calendar-date";
        tdInner.className = "ep-calendar-date-inner";
        // 完整日期
        td.setAttribute("data-date", date.format("YYYY-MM-DD"));
        // 對(duì)應(yīng)的iso星期
        td.setAttribute("data-isoweekday", day);

        // 周末標(biāo)記text.className
        if (day === 6 || day === 7) {
            td.className += " ep-calenday-weekend";
        }
        // 非本月標(biāo)記
        // substr 在ie8下有問題
        // if (month != parseInt(this.currMonth.substr(-2))) {
        if (month != parseInt(this.currMonth.substr(5), 10)) {
            td.className += " ep-calendar-othermonth";
        }
        // 今天標(biāo)記
        if (this.today == date.format("YYYY-MM-DD")) {
            td.className += " ep-calendar-today";
        }

        // 每天渲染時(shí)發(fā)生 還未插入頁(yè)面
        var renderEvent = this.fire("cellRender", {
            // 當(dāng)天的完整日期
            date: date.format("YYYY-MM-DD"),
            // 當(dāng)天的iso星期
            isoWeekday: day,
            // 日歷dom
            el: this.el,
            // 當(dāng)前單元格
            tdEl: td,
            // 日期文本
            dateText: date.date(),
            // 日期class
            dateCls: "ep-calendar-date-text",
            // 需要注入的額外的html
            extraHtml: "",

            isHeader: false
        });

        // 處理對(duì)dayText內(nèi)容和樣式的更改
        text.innerText = renderEvent.dateText;
        text.className = renderEvent.dateCls;

        // 添加新增內(nèi)容
        if (renderEvent.extraHtml) {
            jQuery(renderEvent.extraHtml).appendTo(tdInner);
        }

        currTr.appendChild(renderEvent.tdEl);

        // 每天渲染后發(fā)生 插入到頁(yè)面
        this.fire("afterCellRender", {
            date: date.format("YYYY-MM-DD"),
            isoWeekday: day,
            el: this.el,
            tdEl: td,
            dateText: text.innerText,
            dateCls: text.className,
            extraHtml: renderEvent.extraHtml,
            isHeader: false
        });
    }
}

直接從開始日期往后依次畫出42天即可。

為了靈活性,在繪制的不同時(shí)機(jī)觸發(fā)了不同的事件,在使用時(shí)可綁定相應(yīng)的事件,在其中進(jìn)行個(gè)性化操作。

也為了使用了方便和靈活性,直接在繪制日期時(shí),在相應(yīng)的dom上加入了所對(duì)應(yīng)的日期和星期屬性。

在此過程中需要對(duì)日期是否周末、是否本月、是否是選中的、是否是今天等進(jìn)行相應(yīng)的標(biāo)記處理。

繪制其他內(nèi)容

除了上面所述之外此外還要繪制出年月選擇、標(biāo)題等,這些實(shí)際就是給已經(jīng)有的dom元素中更改內(nèi)容而已,就不再展開了。

切換月份的實(shí)現(xiàn)

上面已經(jīng)基本繪制出了一個(gè)日歷,切換月份實(shí)際就更簡(jiǎn)單了,只用根據(jù)新的月份重新計(jì)算開始日期,清空原來的內(nèi)容,重新進(jìn)行繪制即可。

{
    // 設(shè)置月份
    setMonth: function (ym) {
        var date = moment(ym, "YYYY-MM");

        if (date.isValid()) {
            var oldMonth = this.currMonth,
                aimMonth = date.format("YYYY-MM");

            // 月份變動(dòng)前
            this.fire("beforeMonthChange", {
                el: this.el,
                oldMonth: oldMonth,
                newMonth: aimMonth
            });

            this.currMonth = aimMonth;
            this.render();

            // 月份變動(dòng)后
            this.fire("afterMonthChange", {
                el: this.el,
                oldMonth: oldMonth,
                newMonth: aimMonth
            });

        } else {
            throw new Error(ym + "是一個(gè)不合法的日期");
        }
    }
}
事件的處理

要處理的事件較多,此處僅僅以日期的點(diǎn)擊作為示意。

{
    // 初始化事件
    _initEvent: function () {
        var my = this;
        jQuery(this.el)
            // 日期單元格
            .on("click", ".ep-calendar-date", function (e) {
                var date = this.getAttribute("data-date"),
                    ev = my.fire("dayClick", {
                        ev: e,
                        date: date,
                        day: this.getAttribute("data-isoweekday"),
                        el: my.el,
                        tdEl: this
                    });

                // 如果修改事件對(duì)象的cancel為true后 則不進(jìn)行后續(xù)的選中操作
                if (!ev.cancel) {
                    my.setSelected(date);
                }
            })
    }
}

由于日期所對(duì)應(yīng)的dom元素始終會(huì)添加和移除,直接把事件綁定在日期的dom元素上,則必須在每次新增后重新綁定事件,十分麻煩。

直接使用事件代理機(jī)制,將事件綁定在整個(gè)日歷的dom上即可,這樣事件只用在創(chuàng)建時(shí)初始化一次即可,簡(jiǎn)單、高效、省內(nèi)存。

使用

我們新增這個(gè)控件的主要目的就是要支持在日歷中繪制任意內(nèi)容,怎么使用呢?

var testCalendar = epctrl.init("Calendar", {
    el: "#date",
    // 資源加載過程中的事件需要直接在這里指定
    events: {
        beforeSourceLoad: function (e) {
            // 資源加載前,在加入我們的皮膚樣式文件
            e.cssUrl.push("./test-skin.css");
        }
    }
});
// 日期部分渲染前 支持動(dòng)態(tài)獲取數(shù)據(jù)
testCalendar.on("beforeDateRender", function (e) {
    var startDate = e.startDate,
        endDate = e.endDate;
    // 如果需要?jiǎng)討B(tài)獲取數(shù)據(jù)
    // 則將獲取數(shù)據(jù)的ajax加到事件對(duì)象的ajax屬性上即可
    // 日期渲染的cellRender事件將在ajax成功獲取數(shù)據(jù)后執(zhí)行
    e.ajax = $.ajax({
        url: "getDateInfo.xxx",
        // 將當(dāng)月視圖的開始和結(jié)束時(shí)間傳遞過去
        data: {
            start: startDate,
            end: endDate
        }
    });
});
// 控制渲染過程 可插入任意內(nèi)容或修改原來的內(nèi)容
testCalendar.on("cellRender", function (e) {
    if (!e.isHeader) {
        // 如:周五周六則插入周末 否則插入工作日
        e.extraHtml = "
" + (e.isoWeekday > 5 ? "周末": "工作日") + "
"; } });
總結(jié)

以上就是關(guān)于一個(gè)月視圖日歷控件核心步驟了。

此日歷實(shí)現(xiàn)基于一個(gè)控件基類擴(kuò)展而來,其必要功能僅為一套事件機(jī)制,可參考實(shí)現(xiàn)一套自定義事件機(jī)制

上面只分析了關(guān)鍵步驟,和核心代碼,為了方便使用和擴(kuò)展性,實(shí)際代碼中還要處理很多問題。源碼和文檔如下,感興趣可以閱讀:月視圖日歷

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/92055.html

相關(guān)文章

  • 帶你開發(fā)一個(gè)二維周視圖日歷

    摘要:即之前實(shí)現(xiàn)了一個(gè)月視圖日歷,我們今天來實(shí)現(xiàn)一個(gè)二維周視圖的日歷。難點(diǎn)實(shí)現(xiàn)內(nèi)容部件插入我們實(shí)現(xiàn)這個(gè)二維周視圖日歷的主要目的就是要支持插入任意的內(nèi)容,上面已經(jīng)準(zhǔn)備好了插入內(nèi)容的元素,這里要做的就是將數(shù)據(jù)繪制成放置在合適的位置。 即之前實(shí)現(xiàn)了一個(gè)月視圖日歷,我們今天來實(shí)現(xiàn)一個(gè)二維周視圖的日歷。 以下進(jìn)行分析其中的關(guān)鍵部分。 結(jié)構(gòu)準(zhǔn)備 不同之處在于其在日歷的基礎(chǔ)上還有一個(gè)分類軸,用于展示不同的...

    張金寶 評(píng)論0 收藏0
  • 一款基于移動(dòng)端的日歷控件iantooDate

    摘要:已被移除過時(shí)的提醒時(shí)間的顏色。默認(rèn)當(dāng)日歷控件滑動(dòng)的時(shí)候是否實(shí)時(shí)更新日歷控件的位置主要是對(duì)部分低端機(jī)型做性能處理。返回當(dāng)前的時(shí)間調(diào)用該方法關(guān)閉日歷控件。年月日時(shí)分秒星期 原文鏈接 使用 引入文件: ./build/css/iantooDate.css ./build/js/iantooDate.js 并在頁(yè)面上調(diào)用: iantoo.date() 詳細(xì)使用方法見page/iantooDat...

    liangzai_cool 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<