首页 > 代码库 > javascript动手写日历组件(1)——构建日历逻辑 (by vczero)
javascript动手写日历组件(1)——构建日历逻辑 (by vczero)
一、分析日历的组成部分和交互要素
(1)组成部分:选择年月部分、星期显示、包含本月(或者有前月和下一个月部分日子)
(2)根据选择的年和月份,动态绘制日历面板。
(3)一个日历 7(天) * 5(周) = 35格表格。
(4)一个月份是统一的一个面板;一个月的头一天一定在日历面板的第一行,根据该天的“星期几”确定位置。
(5)第一格子是星期一,最后一个格子是星期日,为5周的日历面板。
二、确定逻辑设计
日历上面的日历,8月1号建军节为什么会出现在这一格?因为一个月的天数是小于5周(35天)的,因此,在第一行肯定包含了一个月份的第一天,而这一天的位置是根据该天的星期几来确定的,比如7月的1号是星期二,那么7月1号在第一行的星期二的位置上,然后依次类推,计算前一个月在面板上显示的日期,下一个月在面板上显示的日期。
可以将每一个表格看成是一个日期对象,那么下一步的目的就是根据传入不同年份和月份,计算出面板上显示的日期对象数组。
三、构建日历类
(1)首先,构建一个可以传入DOM DIV的日历类。
1 var Calendar = function(div){2 this.div = document.getElementById(div);3 };4 5 Calendar.week = [‘星期一‘, ‘星期二‘,‘星期三‘, ‘星期四‘,‘星期五‘, ‘星期六‘, ‘星期日‘];6 Calendar.month = [‘1月‘,‘2月‘,‘3月‘,‘4月‘,‘5月‘,‘6月‘,‘7月‘,‘8月‘,‘9月‘,‘10月‘,‘11月‘,‘12月‘];
(2)日历面板逻辑实现。
一个日历面板包含3个日期对象数组,第一个是preMonth,因为,也许该日历面板上会显示前一个月的一些日期;第二个是本月的日期currentMonth;第三个是下一个月的日期数组,nextMonth。定义一个函数monthPanel代表一个日历面板,返回值是三个数组合并的对象。
如何计算前一个月会显示多少日期?首先计算选择的月份的第一天在第一行(从0开始计算)占据着第几个位置(4),然后上一个月的总天数(31)减去该位置向上索取,即31-4,31-3,31-2,31-1,31.,那么结果就是31-4,31-3,31-2,31-1,31, 1。
如何计算下一个月会显示多少日期?很好办,总共35个格子,那么35 - 当前月份的天数 - 当前月份占据的位置n+1 就是要显示的下一个的日期格子数。
综合后的代码如下:
1 Calendar.prototype.monthPanel = function(date){ 2 //如果传递了Date对象,则按Date对象进行计算月份面板 3 //否则,按照当前月份计算面板 4 var date = date || new Date(), 5 year = date.getFullYear(), 6 month = date.getMonth(), 7 day = date.getDate(), 8 week = date.getDay(), 9 currentDays = new Date(year, month + 1, 0).getDate(),10 preDays = new Date(year, month, 0).getDate(),11 firstDay = new Date(year, month, 1),12 firstCell = firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1,13 bottomCell = 35 - currentDays - firstCell;14 //前一个月该显示多少天15 var preMonth = [];16 for(var p = firstCell; p > 0; p--){17 preMonth.push(new Date(year, month - 1, preDays - p + 1));18 }19 //本月20 var currentMonth = [];21 for(var c = 0; c < currentDays; c++){22 currentMonth.push(new Date(year, month, c + 1));23 }24 //下一个月25 var nextMonth = [];26 for(var n = 0; n < bottomCell; n++){27 nextMonth.push(new Date(year, month + 1, n + 1));28 }29 30 preMonth = preMonth.concat(currentMonth, nextMonth);31 return preMonth;32 };
(3)检测代码逻辑是否正确,正好在使用node cmd,所以索性在node下运行了(当如放到Chrome console也可以),不过需要先注释掉this.div = document.getElementById(div);这一行。var c = new Calendar();console.log(c.monthPanel(new Date(2014, 6, 1))); 其实不传递参数,就是显示当前月份的日历面板。如下图。
三、构建最基础的UI
(1)动态绘制35个表格
1 for(var i = 0; i < 35; i++){ 2 var cellDOM = document.createElement(‘div‘); 3 cellDOM.style.width = cell.width + ‘px‘; 4 cellDOM.style.height = cell.height + ‘px‘; 5 cellDOM.style.display = ‘inline-block‘; 6 cellDOM.style.float = ‘left‘; 7 cellDOM.style.border = ‘1px solid blue‘; 8 cellDOM.style.cursor = ‘pointer‘; this.div.appendChild(cellDOM);11 }
(2)显示日期和月份的头部
1 Calendar.prototype.showUI = function(date){ 2 var width = this.div.style.width || 800, 3 height = this.div.style.height || (600 - 30), 4 cell = {width: (parseInt(width) - 20)/7, height: (parseInt(height) -30 - 20)/5}, 5 monthArr = this.monthPanel(date); 6 7 this.addHeader(date); 8 for(var i = 0; i < 35; i++){ 9 var cellDOM = document.createElement(‘div‘);10 cellDOM.style.width = cell.width + ‘px‘;11 cellDOM.style.height = cell.height + ‘px‘;12 cellDOM.style.display = ‘inline-block‘;13 cellDOM.style.float = ‘left‘;14 cellDOM.style.border = ‘1px solid blue‘;15 cellDOM.style.cursor = ‘pointer‘;16 cellDOM.innerHTML = monthArr[i].getDate();17 this.div.appendChild(cellDOM);18 }19 20 };21 22 Calendar.prototype.addHeader = function(date){23 var header = document.createElement(‘div‘);24 header.style.height = ‘20px‘;25 header.style.width = this.div.style.width || ‘800px‘;26 header.style.textAlign = ‘center‘;27 header.style.fontWeight = ‘bold‘;28 header.innerHTML = date.getFullYear() + ‘年‘ + (date.getMonth() + 1) + ‘月‘;29 console.log(header);30 this.div.appendChild(header);31 }
(3)编写HTML和整个逻辑代码
HTML:
<!doctype html><html> <head> <meta charset="utf-8" /> <script type="text/javascript" src="calendar.js"></script> <script type="text/javascript"> function showCalendar(){ var c = new Calendar(‘calendar‘); c.showUI(new Date()); } </script> </head> <body onload="showCalendar()"> <div id="calendar" style="width:600px; height:400px;border:1px solid red;"></div> </body></html>
javascript:
1 var Calendar = function(div){ 2 this.div = document.getElementById(div); 3 }; 4 5 Calendar.week = [‘星期一‘, ‘星期二‘,‘星期三‘, ‘星期四‘,‘星期五‘, ‘星期六‘, ‘星期日‘]; 6 Calendar.month = [‘1月‘,‘2月‘,‘3月‘,‘4月‘,‘5月‘,‘6月‘,‘7月‘,‘8月‘,‘9月‘,‘10月‘,‘11月‘,‘12月‘]; 7 8 Calendar.prototype.showUI = function(date){ 9 var width = this.div.style.width || 800,10 height = this.div.style.height || (600 - 30),11 cell = {width: (parseInt(width) - 20)/7, height: (parseInt(height) -30 - 20)/5},12 monthArr = this.monthPanel(date);13 14 this.addHeader(date);15 for(var i = 0; i < 35; i++){16 var cellDOM = document.createElement(‘div‘);17 cellDOM.style.width = cell.width + ‘px‘;18 cellDOM.style.height = cell.height + ‘px‘;19 cellDOM.style.display = ‘inline-block‘;20 cellDOM.style.float = ‘left‘;21 cellDOM.style.border = ‘1px solid blue‘;22 cellDOM.style.cursor = ‘pointer‘;23 cellDOM.innerHTML = monthArr[i].getDate();24 this.div.appendChild(cellDOM);25 }26 27 };28 29 Calendar.prototype.addHeader = function(date){30 var header = document.createElement(‘div‘);31 header.style.height = ‘20px‘;32 header.style.width = this.div.style.width || ‘800px‘;33 header.style.textAlign = ‘center‘;34 header.style.fontWeight = ‘bold‘;35 header.innerHTML = date.getFullYear() + ‘年‘ + (date.getMonth() + 1) + ‘月‘;36 console.log(header);37 this.div.appendChild(header);38 }39 40 Calendar.prototype.monthPanel = function(date){41 //如果传递了Date对象,则按Date对象进行计算月份面板42 //否则,按照当前月份计算面板43 var date = date || new Date(),44 year = date.getFullYear(),45 month = date.getMonth(),46 day = date.getDate(),47 week = date.getDay(),48 currentDays = new Date(year, month + 1, 0).getDate(),49 preDays = new Date(year, month, 0).getDate(),50 firstDay = new Date(year, month, 1),51 firstCell = firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1,52 bottomCell = 35 - currentDays - firstCell;53 //前一个月该显示多少天54 var preMonth = [];55 for(var p = firstCell; p > 0; p--){56 preMonth.push(new Date(year, month - 1, preDays - p + 1));57 }58 //本月59 var currentMonth = [];60 for(var c = 0; c < currentDays; c++){61 currentMonth.push(new Date(year, month, c + 1));62 }63 //下一个月64 var nextMonth = [];65 for(var n = 0; n < bottomCell; n++){66 nextMonth.push(new Date(year, month + 1, n + 1));67 }68 69 preMonth = preMonth.concat(currentMonth, nextMonth);70 return preMonth;71 };
基本效果,最为基本的日历逻辑已经完成,可以传入不同的月份,显示日历面板了:
下一篇:增加日历的UI效果 & 添加交互(根据选择,显示日历面板)