在web开发过程中经常会碰到需要选择日期的功能,一般的操作都是在文本框点击,然后弹出日历选择框,直接选择日期就可以在文本框显示选择的日期。开发好之后给用户使用是很方便,但如果每一个日历选择器都要临时开发的话,会严重影响项目进度。所以网上有很多日历插件提供下载使用。

在实际工作中,日历选择器功能确实都是直接使用已开发好的插件。但作为一个前端工程师,还是需要知道具体实例方法,也应该有能力完成此类功能。本实例教程详细讲解怎么样使用原生js,通过面向对象来开发一个日历选择器插件。

一般日历插件使用都非常简单,只需要提供一个input元素就行,其他的工作都是通过插件来完成。本实例也先准备一个input元素待用,如下所示:

<div style="text-align:center;"><input type="text" id="calendarInput">
</div>

原代码可以在《原生js日历选择器插件》下载。

接下来开始封装日历选择器插件。新建 calendar.js 文件,接下来的插件代码都写在此文件中。

需要在body元素中引入 calendar.js 文件,如下所示:

<script src="js/calendar.js"></ script>

把日历选择器功能拆分成一个一个的步骤来完成。

  1. 声明一个日历选择器插件的构造函数,可以传入input元素和配置选项两个参数,如下所示:
//创建日历插件构造函数
function CalendarPlugin(elem,opt={}){}

创建好这个函数之后,先在html文件中调用,这样可以在开发的过程中随时查看效果,如下所示:

  <script src="js/calendar.js"></script><script>//获取文本框var eCalendarInput = document.getElementById('calendarInput');//配置选项var oConfig = {}//调用日历构造函数生成实例对象var oCalenderObj = new CalendarPlugin(eCalendarInput,oConfig);</script>
  1. 在构造函数的原型对象上创建init方法,并在构造函数中调用,如下所示:
//创建日历插件构造函数
function CalendarPlugin(elem,opt={}){//执行初始化this.init();
}//初始化
CalendarPlugin.prototype.init = function(){}
  1. 创建日历插件所需要有元素
    日历插件包括的元素比较多,开发过程中不要急,按照步骤,从上至下一个一个生成元素。
    3.1 创建包裹和input容器等元素,把原文本框移到包裹元素中,并将包裹放到原父元素中,如下所示:
//创建日历插件构造函数
function CalendarPlugin(elem,opt={}){//把文本框设置为日历对象的属性this.eInput = elem;/*...*/
}//初始化
CalendarPlugin.prototype.init = function(){//获取原有intpu元素的父元素var eInputParent = this.eInput.parentNode;//创建日历包裹元素并添加class名称var eWrap = document.createElement('div');eWrap.className = 'calendar_wrap';//创建文本框容器元素var eInputContainer = document.createElement('div');eInputContainer.className = 'calendar_container';//创建清除按钮var eClear = document.createElement('div');eClear.className = 'calendar_clean';//创建日历图标元素var eIcon = document.createElement('div');    eIcon.className = 'calendar_icon';//把日历包裹放到原有父元素中eInputParent.appendChild(eWrap);//文本框容器放到包裹中eWrap.appendChild(eInputContainer);//把相关元素放到文本框容器中eInputContainer.appendChild(this.eInput);eInputContainer.appendChild(eClear);eInputContainer.appendChild(eIcon);//设置文本框为只读this.eInput.setAttribute('readonly',true);}

此时文本框已经变成日历选择器的样式了,如图所示:

3.2 在init方法中创建弹出日历选择框元素

CalendarPlugin.prototype.init = function(){/*...*///创建主要日历容器元素this.eMain = document.createElement('div');this.eMain.className = 'calendar_main';//把日历容器放到包裹元素中eWrap.appendChild(this.eMain);
}

3.3 在日历选择框中添加头部元素、主体元素和底部元素,代码如下:

CalendarPlugin.prototype.init = function(){/*...*///创建日历头部this.eHead = document.createElement('div');this.eHead.className = 'calendar_head';//把日历头部放到日历容器中this.eMain.appendChild(this.eHead);//设置当前年份this.nYear = null;//设置当前月份this.nMonth = null;//设置日历模式,默认显示日历this.sModel = 'date';//创建日历主体this.eBody = document.createElement('div');this.eBody.className = 'calendar_body';//把日历主体放到日历容器中this.eMain.appendChild(this.eBody);//当前选定日期this.selDate = null;//创建底部元素this.eFoot = document.createElement('div');this.eFoot.className = 'calendar_foot';this.eDefine = document.createElement('button');this.eDefine.className = 'define_btn';//把底部元素放到日历容器中this.eMain.appendChild(this.eFoot);this.eFoot.appendChild(this.eDefine);this.eDefine.innerHTML = '今 天';
}

此时效果如图所示:

只有底部有一个“今 天”的按钮,头部年/月和主要的日期都还没有,接下来开始创建这些元素

3.4 创建日历选择框中的头部和日期元素

//初始化
CalendarPlugin.prototype.init = function(){/*...*///生成日历元素this.generateDate();
}//创建头部元素
CalendarPlugin.prototype.generateHead = function(){//根据日历模式不同,组成日历头部html代码var sHeadHtml = '<a class="left_year_btn chang_btn" data-run="lessYear"></a>';if(this.sModel == 'date'){    //日历模式sHeadHtml += `<a class="left_month_btn chang_btn" data-run="lessMonth"></a><a class="year_btn" data-run="showYear" data-model="year">${this.nYear}年</a><a class="month_btn" data-run="showMonth" data-model="month">${+this.nMonth+1}月</a><a class="right_month_btn chang_btn" data-run="addMonth"></a>`;}else if(this.sModel == 'month'){    //月历模式sHeadHtml += `<a class="year_month_btn" data-run="showYear" data-model="year">${this.nYear}年</a>`;}else if (this.sModel == 'year'){    //年历模式sHeadHtml += `<a class="year_month_btn">${+(Math.floor(this.nYear/10)+'0')}-${+(Math.floor(this.nYear/10)+'0')+10}</a>`;}sHeadHtml += '<a class="right_year_btn chang_btn" data-run="addYear"></a>';//填充日历头部this.eHead.innerHTML = sHeadHtml;
}
//生成日历
CalendarPlugin.prototype.generateDate = function(date=null){//组成日历的html代码var sBodyHtml = '<table>';//组合周日 - 周六的列表头sBodyHtml += `<thead><tr><th>日</th><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th></tr></thead>`/*日历中需要记录当前日期、选定日期、面板日期共三个时间*///当前日期:当前日期在日历面板中有一个背景和加粗字体,需要添加class为cur_dayvar dCurDate = new Date();//选定日期:在面板上选择的日期,并且显示在文本框中,有一个背景和白色字体,需要添加class为sel_day//选定日期需要记录在日历实例的selDate属性上,如果暂无选定日期则为当前日期var dSelDate = this.selDate || dCurDate;//初始化当前年/月this.nYear = this.nYear || dSelDate.getFullYear();this.nMonth = this.nMonth || dSelDate.getMonth();//面板上显示的日历var dShowDate = new Date(this.nYear,this.nMonth,dSelDate.getDate());/*日历面板规则:显示42天,6行7列;面板上的第一天是当月1号往前推到星期日。比如当月1号是星期一则上月显示1天、星期三上月显示3天、星期日上月显示7天;*///计算上月要显示的天数var nFrontNum = new Date(this.nYear,this.nMonth,1).getDay() || 7;//日历面板上的日期每增加一个循环周期是一天,获取一天的毫秒数var cycle = 1000*60*60*24;sBodyHtml += '<thbody>'//循环42次for(let i=1;i<43;i++){//以下公式获取日历中每天递增日期的时间戳let dTimes = +dShowDate + cycle * (i-nFrontNum-dShowDate.getDate());//通过时间戳创建Date实例对象let dNewDate = new Date(dTimes);//获取日期添加到html中if((i-1)%7==0){ //判断是否需要换行sBodyHtml += '<tr>';}//判断是否是选定日期,当前日期,面板当月日期,分别加上对应的classsBodyHtml += `<td><a data-time="${dTimes}" class="${this.quiteDate(dNewDate,dSelDate)?'sel_day':this.quiteDate(dNewDate,dCurDate)?'cur_day':dNewDate.getMonth()==this.nMonth?'cur_month':''}">${dNewDate.getDate()}</a></td>`;if(i%7==0){ //判断是否需要结束表格行sBodyHtml += '</tr>';}}sBodyHtml += '</thbody></table>';//填充日历面板this.eBody.innerHTML = sBodyHtml;//生成面板头部this.generateHead();
}
//比较两个日期是否为同一天
CalendarPlugin.prototype.quiteDate = function(d1,d2){var format = 'yyyy-MM-dd';return this.format(d1,format) == this.format(d2,format);
}
//格式化日期
CalendarPlugin.prototype.format = function(date,format){//用于正则表达式的匹配var o =  {"M+" : date.getMonth()+1, //月"d+" : date.getDate(), //日"h+" : date.getHours(), //小时"m+" : date.getMinutes(), //分钟"s+" : date.getSeconds(), //秒};//使用正则将yyyy替换为当前年份if(/(y+)/.test(format)){format = format.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length));}//枚举o对象中匹配的正则,比如MM替换当前月份,dd替换为当前日期for(var k in o)  {if(new RegExp("("+ k +")").test(format)){format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));}}//把格式替换为正确日期后返回return format;
}

此时效果如图所示:

到这一步,日历选择器的元素大部分都已经创建。这段代码新加了几个方法,分别用于创建头部、日期等,因为我都写了详细的注释,这里就不再赘述。接下来需要给元素绑定事件,实现日期变化和选择等。

  1. 添加配置选项

插件一般都会允许开发人员根据项目需求做个性化设置,所以可以自行修改配置,比如设置初始日期、文本框提示、日期格式等。比如在输入框添加提示,可以修改配置选项代码如下:

  //获取文本框/*...*///配置选项var oConfig = {placeholder:'请选择日期',}//调用日历构造函数生成实例对象/*...*/

修改构建函数,在里面添加默认配置选项代码,如下所示:

//创建日历插件构造函数
function CalendarPlugin(elem,opt={}){//把文本框设置为日历对象的属性/*...*///默认配置选项this.oConfig = {format:'yyyy-MM-dd',            //日期格式value:null,                                //默认日期placeholder:''     //文本框提示}//修改为传入的配置选项for(let k in this.oConfig){opt[k] && (this.oConfig[k] = opt[k]);}//执行初始化/*...*/
}

在初始化方法中设置相关配置

//初始化
CalendarPlugin.prototype.init = function(){/*...*///默认赋值this.oConfig.value && (this.eInput.value = this.oConfig.value) && (this.selDate = new Date(this.oConfig.value));//设置文本框提示this.eInput.placeholder = this.oConfig.placeholder;
}

此时可以看到输入框中就有了默认提示信息,如图所示:

5. 添加绑定事件

5.1 输入框绑定点击事件,点击显示日历面板

默认情况下,应该先把日历面板设置隐藏

CalendarPlugin.prototype.init = function(){/*...*///默认隐藏日历面板this.eMain.style.display = 'none';
}

再给输入框添加事件,生成并显示日历。this.generateDate() 方法也应该放到事件中再调用,代码中有详细注释:

//初始化
CalendarPlugin.prototype.init = function(){/*...*///文本框点击显示日历面板this.eInput.addEventListener('click',()=>{if(this.eMain.style.display=='none'){//显示日历面板this.eMain.style.display = 'block';//在页面上绑定点击事件,除日历面板以外任何位置点击鼠标时,隐藏日历面板document.addEventListener('click',hideMain,false);//默认显示日历this.sModel = 'date';//初始化年/月this.nYear = null;this.nMonth = null;//生成日历this.generateDate();}else{//隐藏日历面板hideMain();}});//因为addEventListener监听事件必须是命名函数才能取消,所以在这里创建一个隐藏日历面板函数var eMain = this.eMain;function hideMain(){eMain.style.display = 'none';document.removeEventListener('click',hideMain,false);}//阻止冒泡eWrap.addEventListener('click',function(event){event.stopPropagation();});
}

此时已经实现输入框点击显示日历选择框,空白位置点击隐藏日历选择框功能。

5.2 日期面板绑定点击事件,选择日期或今天按钮,修改文本框的值,并且隐藏日历面板

//初始化
CalendarPlugin.prototype.init = function(){/*...*///日期面板点击事件this.eBody.addEventListener('click',(event)=>{//获取点击的元素let eTarget = event.target;//获取日期时间戳let sTime = eTarget.dataset.time;//获取月let sMonth = eTarget.dataset.month;//获取年let sYear = eTarget.dataset.year;//获取当前元素classNamelet sClass = eTarget.className;if(this.sModel=='date'){    //当前模式是日期,在输入框显示日期,并隐藏日历面板if(sTime && sClass != 'sel_day'){this.selDate = new Date(+sTime);this.eInput.value = this.format(this.selDate,this.oConfig.format);hideMain();}}else{    if(sMonth||sYear){    //年历或月历面板,创建选择的年或月的日期this.nYear = sYear || this.nYear;this.nMonth = sMonth || this.nMonth; this.sModel = 'date';this.generateDate();}}});//点击今天按钮选择今天的日期this.eDefine.addEventListener('click',(event)=>{this.selDate = new Date();this.eInput.value = this.format(this.selDate,this.oConfig.format);hideMain();});
}

此时选择日期后效果如下所示:

5.3 头部元素绑定点击事件,实例修改年、月,选择年、月等功能,代码如下:

//初始化
CalendarPlugin.prototype.init = function(){/*...*///日历面板头部点击事件this.eHead.addEventListener('click',(event)=>{//获取点击的元素let eTarget = event.target;//获取修改方式let sRun = eTarget.dataset.run;//获取面板模式let sModel = eTarget.dataset.model;this.sModel = sModel || this.sModel;if(sRun=='addYear'){    //切换后一年if(this.sModel=='year'){this.nYear+=10;}else{this.nYear++;}}else if(sRun=='lessYear'){    //切换前一年if(this.sModel=='year'){this.nYear-=10;}else{this.nYear--;}}else if(sRun=='addMonth'){    //切换下一个月this.nMonth++;}else if(sRun=='lessMonth'){    //切换前一个月this.nMonth--;}if(this.sModel=='year'){this.generateYear();}else if(this.sModel=='month'){this.generateMonth();}else{this.generateDate(new Date(this.nYear,this.nMonth,1));    //因为切换只年月没选择日期,日期可以任意一天,所以设置为1号}});
}/*...*///生成月历
CalendarPlugin.prototype.generateMonth = function(){//生成月份的html元素let sBodyHtml = '<ul>';for(let i=0;i<12;i++){sBodyHtml += `<li><a data-month="${i+1}" class="${i==this.nMonth?'sel_day':''}">${i+1}月</a></li>`}sBodyHtml += '</ul>';this.eBody.innerHTML = sBodyHtml;    //生成面板头部this.generateHead();
}
//生成年历
CalendarPlugin.prototype.generateYear = function(){//共显示12年,可以通过把当前年最后一个数字改为0获取10年中的第一年let nStart = +(Math.floor(this.nYear/10)+'0');//再从-1开始循环到11,就可以循环出12年//生成年份的html元素let sBodyHtml = '<ul>';for(let i=-1;i<11;i++){sBodyHtml += `<li><a data-year="${i+nStart}" class="${i==-1||i==10?'no_cur':i+nStart==this.nYear?'sel_day':''}">${i+nStart}</a></li>`}sBodyHtml += '</ul>';//修改面板this.eBody.innerHTML = sBodyHtml;    //生成面板头部this.generateHead();
}

日期选择器功能已经基本完成,这已经是一个可以在项目中正常使用的日期选择器,如有疑问或bug,欢迎在留言中提出,感谢!

原生js日历选择器插件开发实例教程相关推荐

  1. html时间选择器时分秒,原生js日历日期控件带时分秒日期选择器

    特效描述:原生js日历日期 时分秒日期选择器.时分秒日期选择 代码结构 1. 引入JS 2. HTML代码 [注意事项] 一.请千万勿移动laydate中的目录结构,它们具有完整的依赖体系.使用时,只 ...

  2. 原生js cookie的使用实例setCookie()、getCookie()方法

    01 <html> 02 <head> 03 <title>原生js cookie的使用实例setCookie().getCookie()方法</title& ...

  3. php日期控件calendar.js,轻量级的原生js日历插件calendar.js使用指南

    使用说明: 需要引入插件calendar.js/calendar.min.js 须要引入calendar.css 样式表,可以自定义自己想要的皮肤 本日历插件支持cmd模块化 如下调用:xvDate( ...

  4. 原生js循环展示dom_web前端教程:JS高阶编程技巧-惰性函数

    框架(framework)是一个框子--指其约束性,也是一个架子--指其支撑性.是一个基本概念上的结构,用于去解决或者处理复杂的问题. Web前端教程 框架这个广泛的定义使用的十分流行,尤其在软件概念 ...

  5. 原生js颜色选择器取色器组件

    文件结构展示 文件结构目录 color-index.js config.js color - picker-index.js 上传太多代码不是很好看,,我上传的都不收费, 完整代码包点击下载如不能下载 ...

  6. 原生 Js 日历挂件

    Css code /************************** 日历样式对应表* #date 日历块 * table 表格* th 头部* td 身体* a.now 本月* a.non-ar ...

  7. 用原生 JS 实现双向绑定及应用实例

    写在前面: 所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更也能实时展现到界面.angular封装了双向绑定的方法,使双向绑定变得十分简单.但是在有些场景下(比如下面那个场景),不能使用 ...

  8. Ajax和JSON-学习笔记01【原生JS方式实现Ajax】

    Java后端 学习路线 笔记汇总表[黑马程序员] Ajax和JSON-学习笔记01[原生JS方式实现Ajax] Ajax和JSON-学习笔记02[JQuery方式实现Ajax] Ajax和JSON-学 ...

  9. 基于jquery插件开发入门教程

    鉴于最近要使用大量的jquery,所以总有一种捣鼓文字来抒发一下情绪的冲动.思前想后就来篇jquery插件开发入门教程吧,毕竟如果不想开发插件,那自己无论用别人的插件多牛逼,最多只是js的使用者,作为 ...

最新文章

  1. paperclip的id partition功能还是相当的给力,省得我们实现了,这个功能之前连想都没想到,...
  2. GitHub 创建项目
  3. android 自定义progressbar demo,Android 自定义进度条ColorfulProgressbar,原理简单、效果还行...
  4. 设计模式实践系列 (3) - 装饰模式 ( Decorator )
  5. Spring Cloud Bus 消息总线实现配置自动刷新
  6. 因设备需求超供应预期 摩托罗拉折叠机Razr推迟在美上市时间
  7. 3.3 CMMI3级——技术解决方案(Technical Solution)
  8. 计算机组成原理经典试卷
  9. 如何在ROS中使用VScode创建功能包并编写cpp文件
  10. 阿里云域名注册及域名解析
  11. 高通SDX55平台:Modem Loopback测试指导
  12. Gaussian Blurring
  13. iOS14降级iOS13
  14. K-Means算法实现网页聚类
  15. 电源学习总结(四)——线性稳扩流方法
  16. R下运行UMAP方案
  17. python爬虫爬取《斗破苍穹》小说(入门必备)
  18. [案例5-1]模拟订单号生成
  19. Abaqus线性动态分析之模态与响应谱分析(附INP文件)
  20. PDF合并后怎么调整顺序?

热门文章

  1. 从键盘输入两个数作为除数和被除数。要求程序中捕获NumberFormatException 异常和ArithmeticException 异常, 而且无论在哪种情况下,“程序执行结束”这句话都会在控制
  2. Python数据分析:实时更新全国全球疫情分析
  3. 红旗linux桌面版反应慢,亲自感受红旗Linux系统优化大全
  4. 阿里云搭建frp(其他云,通用)内网穿透
  5. linux灯控软件下载,Maize DMX下载
  6. xp无法发现win7计算机,什么原因导致windows xp系统电脑无法识别U盘
  7. Downie 4 4.6.12 MAC上最好的一款视频下载工具
  8. 流量红利枯竭是伪命题,VC们全在胡说八道
  9. mysql 时间段分组,MySQL 按时间单位进行分组
  10. Region相关算子