首页 > 代码库 > 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效果 & 添加交互(根据选择,显示日历面板)