热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

原生JS实现日历组件的示例代码

想要实现的效果 点击日期选择框出现日历 有个日期控制栏帮助选择日期, 包括年、月、日的选择和今天 日历格子,初次点击日

想要实现的效果

  • 点击日期选择框出现日历
  • 有个日期控制栏帮助选择日期, 包括年、月、日的选择和今天
  • 日历格子,初次点击日期选择框时显示此刻的日期,日历格子的日期应该包括这个月的所有天数,同时如果当月的1号不是周日,还应补全从周日到1号的天数。还要在这个月最后1号的后面补全到周六。
  • 日期控制栏和日历格子的日期还有选择框里的日期的变化要是同步的。

实现思路

为了组件的可复用性,需要用面向对象的思想。

每个日历组件都是一个日历对象,主要包括日期选择框,日期控制显示栏,还有日历格子,为了保持日期控制显示栏和日历格子日期同步变化,日期控制栏和日历里面的每个格子都应该包含一个Date属性,点击日历里的格子,将格子存的Date属性作为函数参数,调用函数改变日期控制栏显示的时间。同理,日期控制栏时间变化时,也将Date属性作为参数调用函数,函数重新绘制日历格子。

上码:

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); 
  },
}

初始化日期控制栏:

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='
'+nowYear+'年
    '; for(var i=0;i<150;i++){ contentStr+='
  • '+(i+1900)+'年
  • '; } contentStr+='
' +'
' +'
' +'
' +'' +'
'; var weekday = ['日', '一', '二', '三', '四', '五', '六']; for (var i = 0; i <7; i++) { contentStr+=''+weekday[i]+''; } contentStr+='
'; 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'&#63; ul.style.display='inline-block':ul.style.display='none'; }; //为年选择下拉框绑定点击事件 var yearSelectBox = yearInput.lastChild; var yearLi = yearSelectBox.children; for(var i=0;i0){ 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

drawShowDateBar函数为日期控制栏的年份、月份、和天的点击按钮设置了点击事件处理函数。还有选择下拉框变化的处理函数。

在日期控制栏初始化时,或者改变showDateBar的Date时,都会调用changeShowDateBar 函数。这个函数主要根据传入的日期改变日期控制栏“日”下拉栏的天数,因为每个月的天数不尽相同,所以要根据传入的日期来改变。会计算出传入的日期对应的月份有多少天,使用getDaysOfMonth函数计算。

//计算一个月的天数
  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));
  },
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+='';
    }
    daysOptions.innerHTML = dayStr;
  // console.log(date.toLocaleDateString()+'changeShowDateBar');
    daysOptions.selectedIndex = date.getDate()-1;
    this.drawPicker(date);
  },

在日期控制栏的Date变化后,日历格子的日期也应该要改变,显示的日期要和日期控制栏的保持一致。所以在changeShowDateBar函数结尾处调用drawPicker函数,重新绘制日历格子。

绘制日历格子的思路

drawPicker函数要根据传入的日期绘制日历格子。

  • 首先计算传入的日期月份的天数
  • 计算这个月1号是周几 。利用Date对象的date.setDate(1) //将天设置为1号 。date.getDay() //得到这天是周几
  • 如果1号不是周日,则补全周日到1号的天数。可以利用oldDate.setDate(-1) //设置日期为原来日期的上个月的最后一天。注意setDate是会改变当前日期的,并不是返回新的日期。
  • 从1号到这个月最后一天循环。
  • 补全最后一天到周六的天数

drawPicker函数:

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 
//日期框点击时显示日历
  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&#63;("0"+month):month)+"-"+(date<10&#63;("0"+date):date);
  },

实现效果


有点丑......

实现中遇到的问题

  • 日历格子的绘制问题 。要补全1号前面到周日的天数,还要补全当月最后1号到周六的天数。日历格子的绘制可以分为3部分,当月前面、当月和当月后面的。要计算出1号是周几,然后将这周周日到1号的天数绘制。
  • 当月的日历从1号到最后1号循环绘制。补全最后1号到周六的天数(date.getDay()<=6)
  • 日历格子和日期控制栏显示的同步。在绘制时为每个日历格子单元保存其代表的Date。点击格子单元时,调用changeShowDateBar函数,将单元存的Date传入,改变日期控制栏显示的日期,然后重绘日历格子。
  • 每个月天数不同,出现的“日”选择框天数不同的问题。在changeShowDateBar函数里会根据传入的Date,计算当月有多少天,然后动态生成“日”选择框应有的天数。
  • 跨月,跨年的处理。在日期控制栏中,有月份和日的上下按钮,在处理跨月和跨年时,判断这月(日)是否为最后一月(日),若为,则日期控制栏的Date的年(月)加1,将显示的月(日)设为第一月(日),调用changeShowDateBar函数。同理判断是否为第一月(日)。

用到的Date API

  • date.getFullYear() //得到date的年份
  • date.getMonth() //得到月份 0-11
  • date.getDate() //得到日期 1-31的数字
  • date.getDay() // 得到这天是周几 0-6
  • date.getTime()// 得到date的时间戳 ms表示
  • date.setFullYear(2017); // 设置年份
  • date.setMonth(x) // 如果设置为0-11,则date为x年的1-12月,如果比11大,则会往前面推,会跳到x+([(n+1)/12])年的第(n+1)%12个月
  • 如果为负数,例如-1则会调到上一年的最后一月去。
  • date.setDate(x) // 和setMonth是同理的,它会自动根据当月的天数,判断是否发生月份的变动。-1代表date跳到上月的最后一天
  • date.setTime()// 根据时间戳设置date

项目源码 https://github.com/wenkeShi/js-calendar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文详细介绍了如何使用Python编写爬虫程序,从豆瓣电影Top250页面抓取电影信息。文章涵盖了从基础的网页请求到处理反爬虫机制,再到多页数据抓取的全过程,并提供了完整的代码示例。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 2023 ARM嵌入式系统全国技术巡讲旨在分享ARM公司在半导体知识产权(IP)领域的最新进展。作为全球领先的IP提供商,ARM在嵌入式处理器市场占据主导地位,其产品广泛应用于90%以上的嵌入式设备中。此次巡讲将邀请来自ARM、飞思卡尔以及华清远见教育集团的行业专家,共同探讨当前嵌入式系统的前沿技术和应用。 ... [详细]
  • 程序员妻子吐槽:丈夫北漂8年终薪3万,存款情况令人意外
    一位程序员的妻子在网上分享了她丈夫在北京工作八年的经历,月薪仅3万元,存款情况却出乎意料。本文探讨了高学历人才在大城市的职场现状及生活压力。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 本文介绍了如何使用JQuery实现省市二级联动和表单验证。首先,通过change事件监听用户选择的省份,并动态加载对应的城市列表。其次,详细讲解了使用Validation插件进行表单验证的方法,包括内置规则、自定义规则及实时验证功能。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 本文介绍如何使用 Sortable.js 库实现元素的拖拽和位置交换功能。Sortable.js 是一个轻量级、无依赖的 JavaScript 库,支持拖拽排序、动画效果和多种插件扩展。通过简单的配置和事件处理,可以轻松实现复杂的功能。 ... [详细]
  • 本文详细介绍了W3C标准盒模型和IE传统盒模型的区别,探讨了CSS3中box-sizing属性的使用方法及其在布局中的重要性。通过实例分析,帮助读者更好地理解和应用这一关键概念。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
author-avatar
天地菲人间_984
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有