想要实现的效果
实现思路
为了组件的可复用性,需要用面向对象的思想。
每个日历组件都是一个日历对象,主要包括日期选择框,日期控制显示栏,还有日历格子,为了保持日期控制显示栏和日历格子日期同步变化,日期控制栏和日历里面的每个格子都应该包含一个Date属性,点击日历里的格子,将格子存的Date属性作为函数参数,调用函数改变日期控制栏显示的时间。同理,日期控制栏时间变化时,也将Date属性作为参数调用函数,函数重新绘制日历格子。
上码:
</>code
- function Calendar(parentId) {
- this.parentElement = document.getElementById(parentId);
- this.init();
- }
- Calendar.prototype = {
- init: function() {
- this.contains = document.createElement("div");
- this.contains.onselectstart = function(){return false}; //让按钮点击时不会出现文字被选中的蓝色块
- this.dateInput = document.createElement("input");
- this.datePicker = document.createElement("div");
- this.showDateBar = document.createElement("div");
- this.dateBox = document.createElement("div");
- this.icon = document.createElement("i");
- this.contains.className = 'datepicker-container';
- this.dateInput.className = 'date-input';
- this.dateInput.readOnly = true;
- var parent = this;
- this.dateInput.onclick = function(event){
- parent.onDateInputClick(event); //点击日期选择框时显示日历格子
- };
- this.contains.onblur = function(){
- parent.datePicker.style.display = 'none';
- }
- this.datePicker.className = 'date-picker';
- this.datePicker.style.display = 'none';
- this.showDateBar.className = 'show-date';
- this.dateBox.className = 'date-box';
- this.icon.className = 'date-icon';
- this.icon.innerHTML = '\'; //iconfont这里用的阿里图标,可以自行替换
- this.datePicker.appendChild(this.showDateBar);
- this.datePicker.appendChild(this.dateBox);
- this.contains.appendChild(this.dateInput);
- this.contains.appendChild(this.icon);
- this.contains.appendChild(this.datePicker);
- this.parentElement.appendChild(this.contains);
- },
- }
初始化日期控制栏:
</>code
- drawShowDateBar: function(parentElement){
- var parent = this;
- var nowDate = new Date();
- parentElement.date = nowDate;
- var nowYear = nowDate.getFullYear();
- var nowMonth = nowDate.getMonth();
- var nowDay = nowDate.getDate();
- //showDateBar内容拼接
- var contentStr ='<div class="year-input"><span>'+nowYear+'年</span><i class="select-year-btn">Z</i><ul class="year-select-box" style="display : none">';
- for(var i=0;i<150;i++){
- contentStr+='<li>'+(i+1900)+'年</li>';
- }
- contentStr+='</ul></div>'
- +'<div class="month-input"><i class="prev-month">[</i><select class="months-options">'
- for(var i=0;i<12;i++){
- contentStr+='<option>'+(i+1)+'月</option>';
- }
- contentStr+='</select><i class="next-month">\</i></div>'
- +'<div class="day-input"><i class="prev-day">[</i><select class="days-options"></select>'
- +'<i class="next-day">\</i></div>'
- +'<button class="today-btn">今天</button>'
- +'<div class="days-title">';
- var weekday = ['日', '一', '二', '三', '四', '五', '六'];
- for (var i = 0; i < 7; i++) {
- contentStr+='<span class="day-title">'+weekday[i]+'</span>';
- }
- contentStr+='</div>';
- parentElement.innerHTML = contentStr;
- this.changeShowDateBar(nowDate); //插入到showTimeBar之后,初始化,传入的参数是现在的时间
- var yearInput = parentElement.firstChild;
- //年选择框点击显示和隐藏选择列表
- yearInput.onclick = function(){ //target和this的区别 target是触发事件的元素,this是处理事件的元素
- var ul = this.lastChild;
- ul.style.display==='none'||ul.style.display==='none'? ul.style.display='inline-block':ul.style.display='none';
- };
- //为年选择下拉框绑定点击事件
- var yearSelectBox = yearInput.lastChild;
- var yearLi = yearSelectBox.children;
- for(var i=0;i<yearLi.length;i++){
- yearLi[i].onclick = function(){
- parent.showDateBar.date.setFullYear(this.innerText.slice(0,-1));
- parent.changeShowDateBar(parent.showDateBar.date); //时间改变之后都要重新调用,因为不同年,不同月,某个月的天数不全一样
- };
- }
- //为month的前后按钮添加点击事件
- var monthInput = yearInput.nextSibling;
- monthInput.firstChild.onclick = function(){
- var monthOptions = this.nextSibling;
- if(monthOptions.selectedIndex>0){
- parent.showDateBar.date.setMonth(--monthOptions.selectedIndex);
- }else{
- monthOptions.selectedIndex = 11;
- parent.showDateBar.date.setFullYear(parent.showDateBar.date.getFullYear()-1);
- parent.showDateBar.date.setMonth(11);
- }
- parent.changeShowDateBar(parent.showDateBar.date);
- };
- monthInput.lastChild.onclick = function(){
- var monthOptions = this.previousSibling;
- if(monthOptions.selectedIndex<11){
- parent.showDateBar.date.setMonth(++monthOptions.selectedIndex);
- }else{
- monthOptions.selectedIndex = 0;
- parent.showDateBar.date.setFullYear(parent.showDateBar.date.getFullYear()+1);
- parent.showDateBar.date.setMonth(0);
- }
- parent.changeShowDateBar(parent.showDateBar.date);
- }
- monthInput.children[1].onchange = function(){
- parent.showDateBar.date.setMonth(this.selectedIndex);
- parent.changeShowDateBar(parent.showDateBar.date)
- };
- //为day的前后按钮添加点击事件
- var dayInput = monthInput.nextSibling;
- dayInput.firstChild.onclick = function(){
- var dayOptions = this.nextSibling;
- if(dayOptions.selectedIndex>0){
- parent.showDateBar.date.setDate(dayOptions.selectedIndex--);
- }else{
- parent.showDateBar.date.setMonth(parent.showDateBar.date.getMonth()-1);
- parent.showDateBar.date.setDate(parent.getDaysOfMonth(parent.showDateBar.date));
- }
- parent.changeShowDateBar(parent.showDateBar.date);
- };
- dayInput.lastChild.onclick = function(){
- var dayOptions = this.previousSibling;
- if(dayOptions.selectedIndex < dayOptions.length-1){
- dayOptions.selectedIndex++;
- parent.showDateBar.date.setDate(dayOptions.selectedIndex+1);
- }else{
- parent.showDateBar.date.setDate(1);
- parent.showDateBar.date.setMonth(parent.showDateBar.date.getMonth()+1);
- }
- parent.changeShowDateBar(parent.showDateBar.date);
- };
- dayInput.children[1].onchange = function(){
- parent.showDateBar.date.setDate(this.selectedIndex+1);
- parent.changeShowDateBar(parent.showDateBar.date)
- };
- //为今天按钮绑定点击事件
- var todayBtn = dayInput.nextSibling;
- todayBtn.onclick = function(){
- parent.drawPicker(new Date());
- parent.changeShowDateBar(new Date());
- }
- },
drawShowDateBar函数为日期控制栏的年份、月份、和天的点击按钮设置了点击事件处理函数。还有选择下拉框变化的处理函数。
在日期控制栏初始化时,或者改变showDateBar的Date时,都会调用changeShowDateBar 函数。这个函数主要根据传入的日期改变日期控制栏“日”下拉栏的天数,因为每个月的天数不尽相同,所以要根据传入的日期来改变。会计算出传入的日期对应的月份有多少天,使用getDaysOfMonth函数计算。
</>code
- //计算一个月的天数
- getDaysOfMonth: function(primalDate) {
- var date = new Date(primalDate); //要新建一个对象,因为会改变date
- var month = date.getMonth();
- var time = date.getTime(); //计算思路主要是month+1,相减除一天的毫秒数
- var newTime = date.setMonth(month + 1);
- return Math.ceil((newTime - time) / (24 * 60 * 60 * 1000));
- },
</>code
- changeShowDateBar : function(date){
- var yearInput = this.showDateBar.firstChild;
- var monthInput = yearInput.nextSibling;
- var dayInput = monthInput.nextSibling;
- yearInput.firstChild.innerText = date.getFullYear()+'年';
- var monthsOptions = monthInput.firstChild.nextSibling;
- monthsOptions.selectedIndex = date.getMonth();
- var daysOptions = dayInput.firstChild.nextSibling;
- var days = this.getDaysOfMonth(date);
- var dayStr = '';
- for(var i=1;i<=days;i++){
- dayStr+='<option>'+i+'日</option>';
- }
- daysOptions.innerHTML = dayStr;
- // console.log(date.toLocaleDateString()+'changeShowDateBar');
- daysOptions.selectedIndex = date.getDate()-1;
- this.drawPicker(date);
- },
在日期控制栏的Date变化后,日历格子的日期也应该要改变,显示的日期要和日期控制栏的保持一致。所以在changeShowDateBar函数结尾处调用drawPicker函数,重新绘制日历格子。
绘制日历格子的思路
drawPicker函数要根据传入的日期绘制日历格子。
drawPicker函数:
</>code
- drawPicker: function(primalDate) {
- var date = new Date(primalDate); //要新建一个对象,因为会改变date
- var nowMonth = date.getMonth()+1;
- var nowDate = date.getDate();
- var spanContainer = [];
- var dateBox = this.dateBox;
- dateBox.innerHTML = '';
- var time = date.getTime();
- var days = this.getDaysOfMonth(date); //计算出这个月的天数
- date.setDate(1); //将date的日期设置为1号
- var firstDay = date.getDay(); //知道这个月1号是星期几
- for (var i = 0; i < firstDay; i++) { //如果1号不是周日(一周的开头),则在1号之前要补全
- var tempDate = new Date(date);
- tempDate.setDate(i - firstDay + 1);
- var span = document.createElement("span");
- span.className = "unshow";
- spanContainer.push({span : span, date : tempDate});
- }
- for (var i = 1; i <= days; i++) { //1号到这个月最后1天
- var span = document.createElement("span");
- span.className = 'show';
- spanContainer.push({span : span, date : new Date(date)});
- date.setDate(i + 1);
- }
- for (var i = date.getDay(); i <= 6; i++) { //在这个月最后一天后面补全
- var span = document.createElement("span");
- span.className = "unshow";
- spanContainer.push({span : span, date : new Date(date)});
- date.setDate(date.getDate()+1);
- }
- for(var i=0;i<spanContainer.length;i++){
- var spanBox = spanContainer[i];
- var span = spanBox.span;
- span.year = spanBox.date.getFullYear(); //为每个span元素添加表示时间的属性
- span.month = spanBox.date.getMonth() + 1;
- span.date = spanBox.date.getDate();
- span.innerText = spanBox.date.getDate();
- if(span.date === nowDate&&span.month === nowMonth) //如果这个span的日期为与传入的日期匹配,设置类名为select
- span.className+=" select";
- var parent = this;
- span.onclick = function(){ //设置点击事件
- var target = event.target;
- var selected = target.parentElement.getElementsByClassName("select");
- for(var i=0 ;i<selected.length;i++){
- selected[i].className = selected[i].className.replace(" select","");
- };
- target.className+=" select";
- parent.changeDate(target.year, target.month, target.date);
- parent.changeShowDateBar(new Date(target.year, target.month-1, target.date));
- };
- dateBox.appendChild(span); //将span添加到dateBox中
- }
- this.changeDate(primalDate.getFullYear(), primalDate.getMonth()+1, primalDate.getDate())
- return;
- },
</>code
- //日期框点击时显示日历
- onDateInputClick: function(event) {
- var target = event.target;
- var value = target.value;
- var datePicker = this.datePicker;
- if(datePicker.style.display==='none'){ //这里必须要在js文件里将datePicker.style.display设置为none,如果是在css文件里设置为none,得到的display为""
- datePicker.style.display = 'block';
- }else{
- datePicker.style.display = 'none';
- return;
- }
- if (!value) this.drawShowDateBar(this.showDateBar); //绘制日历的显示栏
- },
- changeDate : function(year, month, date){
- this.dateInput.value = year+"-"+(month<10?("0"+month):month)+"-"+(date<10?("0"+date):date);
- },
实现效果
有点丑......
实现中遇到的问题
用到的Date API
项目源码 https://github.com/wenkeShi/js-calendar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持365建站网。
如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛