摘要:直接使用事件代理機制,將事件綁定在整個日歷的上即可,這樣事件只用在創建時初始化一次即可,簡單高效省內存。
首發我的博客 - https://blog.cdswyda.com/post/2017121010
日歷控件多的不勝枚舉,為什么我們還要再造一個輪子呢?
因為大多數日歷控件都是用于選擇日期的,有種需求是要在日歷上展示各種各樣的內容,這樣的日歷控件較少,而且試用下來并不滿意。
因此就再造一個輪子,現在帶你一起基于使用之前完成的組件機制來開發一個日歷控件。
需求
簡單把需求整理如下:
月視圖
支持在日歷中每一天中插入任意的內容
相關點擊事件
獲取日歷當前視圖的開始和結束日期
獲取設置選中的日期
實現分析首先我們拿系統中自帶的日歷觀察一下,看看日歷的特征到底是怎么樣的。
一個月中有 28 到 31 天不等,但是為了保證完整的結構,日歷中會有部分上一月和下一月的日期,總結下來,一個月中顯示的必定是整整6周的日期。
那么只要得到當月的開始日期就可以繪制日歷了。
如何計算當月日歷視圖中的開始日期呢? 前面已經分析了,為了保證完整,它顯示了上一月的部分天數,那么只用從當月的1號開始往前推算就可以了。
開始日期 = 當月1號的日期 - 當月1號的星期 結束日期 = 開始日期 + 42天
這個問題搞清楚了,感覺實現這么一個日歷就沒什么大阻礙了,開始動工吧!
必要結構準備首先構建如下所示的基本結構
其中:
頭部左右為個性化區域,用于實際使用時放置任意內容。中間用于顯示當前月份和切換按鈕
主體區域中用繪制整個日歷
thead 中繪制周一至周日 或周日至周一的星期,這段內容是不會隨月份切換而改變的,可以直接準備好
tbody 中用于繪制可變的日期,準備好容器留空即可。
腳部區域用于實際使用時放置任意各項化內容
menu區域用于切換日期時彈出的面板
繪制日歷在初始化好日歷結構后就可以開始繪制日歷了。
計算一個月中的開始日期和結束日期首先完成開始和結束時間的計算
{ // 初始化當前月份的開始日期和結束日期 _initStartEnd: function () { // 當月1號 var currMonth = moment(this.currMonth, "YYYY-MM"), // 當月1號是周幾 the ISO day of the week with 1 being Monday and 7 being Sunday. firstDay_weekday = currMonth.isoWeekday(), startDateOfMonth, endDateOfMonth; if (!this.dayStartFromSunday) { // 開始為周一 則向前減少周幾的天數-1即為 開始的日期 startDateOfMonth = currMonth.subtract(firstDay_weekday - 1, "day"); } else { // 開始為周日 則直接向前周幾的天數即可 startDateOfMonth = currMonth.subtract(firstDay_weekday, "day"); } endDateOfMonth = startDateOfMonth.clone().add(41, "day"); this.startDateOfMonth = startDateOfMonth; this.endDateOfMonth = endDateOfMonth; } }
由于要處理很多日期,而JavaScript中關于日期處理時,不同瀏覽器下差異較大,因此直接使用 moment.js 來對日期進行統一處理。
由于使用習慣不同,一周的開始到底是周一還是周日是不確定的,因此直接作為配置即可。
繪制一月中的日期上面已經計算得到了一個月的開始日期和結束日期,那么只用遍歷進行繪制即可。
由于我們使用了表格實現,因此需要按行繪制。
實現如下:
{ // 日歷可變部分的渲染 _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")); // 對應的iso星期 td.setAttribute("data-isoweekday", day); // 周末標記text.className if (day === 6 || day === 7) { td.className += " ep-calenday-weekend"; } // 非本月標記 // substr 在ie8下有問題 // if (month != parseInt(this.currMonth.substr(-2))) { if (month != parseInt(this.currMonth.substr(5), 10)) { td.className += " ep-calendar-othermonth"; } // 今天標記 if (this.today == date.format("YYYY-MM-DD")) { td.className += " ep-calendar-today"; } // 每天渲染時發生 還未插入頁面 var renderEvent = this.fire("cellRender", { // 當天的完整日期 date: date.format("YYYY-MM-DD"), // 當天的iso星期 isoWeekday: day, // 日歷dom el: this.el, // 當前單元格 tdEl: td, // 日期文本 dateText: date.date(), // 日期class dateCls: "ep-calendar-date-text", // 需要注入的額外的html extraHtml: "", isHeader: false }); // 處理對dayText內容和樣式的更改 text.innerText = renderEvent.dateText; text.className = renderEvent.dateCls; // 添加新增內容 if (renderEvent.extraHtml) { jQuery(renderEvent.extraHtml).appendTo(tdInner); } currTr.appendChild(renderEvent.tdEl); // 每天渲染后發生 插入到頁面 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天即可。
為了靈活性,在繪制的不同時機觸發了不同的事件,在使用時可綁定相應的事件,在其中進行個性化操作。
也為了使用了方便和靈活性,直接在繪制日期時,在相應的dom上加入了所對應的日期和星期屬性。
在此過程中需要對日期是否周末、是否本月、是否是選中的、是否是今天等進行相應的標記處理。
繪制其他內容除了上面所述之外此外還要繪制出年月選擇、標題等,這些實際就是給已經有的dom元素中更改內容而已,就不再展開了。
切換月份的實現上面已經基本繪制出了一個日歷,切換月份實際就更簡單了,只用根據新的月份重新計算開始日期,清空原來的內容,重新進行繪制即可。
{ // 設置月份 setMonth: function (ym) { var date = moment(ym, "YYYY-MM"); if (date.isValid()) { var oldMonth = this.currMonth, aimMonth = date.format("YYYY-MM"); // 月份變動前 this.fire("beforeMonthChange", { el: this.el, oldMonth: oldMonth, newMonth: aimMonth }); this.currMonth = aimMonth; this.render(); // 月份變動后 this.fire("afterMonthChange", { el: this.el, oldMonth: oldMonth, newMonth: aimMonth }); } else { throw new Error(ym + "是一個不合法的日期"); } } }事件的處理
要處理的事件較多,此處僅僅以日期的點擊作為示意。
{ // 初始化事件 _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 }); // 如果修改事件對象的cancel為true后 則不進行后續的選中操作 if (!ev.cancel) { my.setSelected(date); } }) } }
由于日期所對應的dom元素始終會添加和移除,直接把事件綁定在日期的dom元素上,則必須在每次新增后重新綁定事件,十分麻煩。
直接使用事件代理機制,將事件綁定在整個日歷的dom上即可,這樣事件只用在創建時初始化一次即可,簡單、高效、省內存。
使用我們新增這個控件的主要目的就是要支持在日歷中繪制任意內容,怎么使用呢?
var testCalendar = epctrl.init("Calendar", { el: "#date", // 資源加載過程中的事件需要直接在這里指定 events: { beforeSourceLoad: function (e) { // 資源加載前,在加入我們的皮膚樣式文件 e.cssUrl.push("./test-skin.css"); } } }); // 日期部分渲染前 支持動態獲取數據 testCalendar.on("beforeDateRender", function (e) { var startDate = e.startDate, endDate = e.endDate; // 如果需要動態獲取數據 // 則將獲取數據的ajax加到事件對象的ajax屬性上即可 // 日期渲染的cellRender事件將在ajax成功獲取數據后執行 e.ajax = $.ajax({ url: "getDateInfo.xxx", // 將當月視圖的開始和結束時間傳遞過去 data: { start: startDate, end: endDate } }); }); // 控制渲染過程 可插入任意內容或修改原來的內容 testCalendar.on("cellRender", function (e) { if (!e.isHeader) { // 如:周五周六則插入周末 否則插入工作日 e.extraHtml = "總結" + (e.isoWeekday > 5 ? "周末": "工作日") + ""; } });
以上就是關于一個月視圖日歷控件核心步驟了。
此日歷實現基于一個控件基類擴展而來,其必要功能僅為一套事件機制,可參考實現一套自定義事件機制
上面只分析了關鍵步驟,和核心代碼,為了方便使用和擴展性,實際代碼中還要處理很多問題。源碼和文檔如下,感興趣可以閱讀:月視圖日歷
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92055.html
摘要:即之前實現了一個月視圖日歷,我們今天來實現一個二維周視圖的日歷。難點實現內容部件插入我們實現這個二維周視圖日歷的主要目的就是要支持插入任意的內容,上面已經準備好了插入內容的元素,這里要做的就是將數據繪制成放置在合適的位置。 即之前實現了一個月視圖日歷,我們今天來實現一個二維周視圖的日歷。 以下進行分析其中的關鍵部分。 結構準備 不同之處在于其在日歷的基礎上還有一個分類軸,用于展示不同的...
摘要:已被移除過時的提醒時間的顏色。默認當日歷控件滑動的時候是否實時更新日歷控件的位置主要是對部分低端機型做性能處理。返回當前的時間調用該方法關閉日歷控件。年月日時分秒星期 原文鏈接 使用 引入文件: ./build/css/iantooDate.css ./build/js/iantooDate.js 并在頁面上調用: iantoo.date() 詳細使用方法見page/iantooDat...
閱讀 1459·2021-09-30 09:57
閱讀 1466·2021-09-09 09:33
閱讀 2219·2021-09-04 16:40
閱讀 1792·2021-09-01 10:50
閱讀 3233·2021-09-01 10:31
閱讀 2539·2019-08-30 15:56
閱讀 2969·2019-08-30 15:44
閱讀 3474·2019-08-29 17:29