JS日期选择器

Posted MirrorSpace

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS日期选择器相关的知识,希望对你有一定的参考价值。

浏览器自带

浏览器自带日期控件,使用<input type="date">时,点击后会弹出.

1:EDGE 2:火狐 3:谷歌  三种都不一样.略胜于无

练习

模仿火狐日期控件的外观实现一个日期插件.效果

 PC

 

手机

使用控件

// 日期
<input onclick="MyDatePick()" />
// 日期加时间
<input onclick="MyDatePick({fmt:\'datetime\'})" />

 解决问题

  1.将日期框分四行区域,第一行是年,月,今天. 第二行是周,像是一个表格标题头.第三行是六行七列的日(天),第四行是时间.

  2.日期插件类生成整个DOM,绑定事件.css兼容手机和PC.

  3.六行七列的天,起点是选定月份的1号,再往前推到最近的周日.终点是选定月份的最后一天,再往后推到最近的周六.

  4.pc端可以手动输入input,手机端只点选,不能弹出键盘.

风格

  样式模仿火狐版本的日期框.实现最基本的选择日期到输入框功能.

  手机端固定显示在手机中间,占满宽度,按钮尺寸调大了些,便于手指点击.pc端显示在input框下方,对齐input左边.

  天排列由周日到周六,共42天.年份,月份,时分秒按钮弹出的选项面板同时只能显示一个.点击控件空白处可关闭.

  手机端控件得到焦点,input框点击后,不弹出键盘.pc端控件无焦点,点击控件外区域时关闭控件.

  年份选项区范围1900-2100  手工输入(INPUT框)年份0-9999可识别.时分秒范围0-23 0-59.

  不属于选定年月份中的天,选中的天,今天.在颜色上有区分

js

  1 ; (function ()
  2 {
  3     // window对象上使用的名字
  4     let exportName = \'MyDatePick\';
  5     // datebox类名
  6     let dateboxCls = \'date-box\';
  7     // 触发日期框的INPUT的JQ对象引用
  8     let inputJQ = null;
  9     // 日期框JQ对象
 10     let dateboxJQ = null;
 11     // 日期框运行时数据
 12     let cfg = null;
 13 
 14     // 在input上使用此方法. <input onclick="MyDatePick()" />,需要时间部分: MyDatePick({fmt:datetime})
 15     let mydate = function (config)
 16     {
 17         let event = window.event || arguments.callee.caller.arguments[0]; // 获取event对象
 18         event.stopPropagation();
 19         let input = event.currentTarget;
 20         // 初始化已选年月日
 21         initDate(input, config);
 22         // 生成DOM
 23         let datedom = createDom();
 24         // 显示
 25         showDateBox(datedom);
 26         // 绑定事件
 27         dateboxJQ = $(\'.\' + dateboxCls).eq(0);
 28         bindEventForShow();
 29     }
 30 
 31     // 初始化:已选年月,保存日期框的INPUT的JQ对象引用
 32     let initDate = function (input, config)
 33     {
 34         // input的JQ对象
 35         inputJQ = $(input);
 36 
 37         // 用inpupt的值初始化时间,为空则默认今天时间.input时间格式只支持 yyyy-MM-dd HH:mm:ss(时间,秒部分可省略)
 38         let inputval = $.trim(input.value);
 39         if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(inputval))
 40         {
 41             inputval = inputval + \' 00:00:00\';
 42         } else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$/.test(inputval))
 43         {
 44             inputval = inputval + \':00\';
 45         }
 46         else if (/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(inputval))
 47         { }
 48         else
 49         {
 50             inputval = null;
 51         }
 52         // console.log(inputval);
 53         // 不带时间部分的日期串,用parse解后,会有时差.
 54         let inputDate = Date.parse(inputval);
 55         let date = isNaN(inputDate) ? new Date((new Date()).setHours(0, 0, 0)) : new Date(inputDate);
 56         //
 57         //console.log(date);
 58         cfg = {};
 59         cfg.year = date.getFullYear();
 60         cfg.month = date.getMonth();
 61         cfg.day = date.getDate();
 62         cfg.hour = date.getHours();
 63         cfg.minute = date.getMinutes();
 64         cfg.second = date.getSeconds();
 65         // 显示格式为日期(\'yyyy-MM-dd\'),或者日期和时间(\'yyyy-MM-dd HH:mm:ss\')
 66         cfg.dateFmt = \'yyyy-MM-dd\';
 67         cfg.fmtType = 1;
 68         if (config && config.fmt == \'datetime\')
 69         {
 70             cfg.dateFmt = \'yyyy-MM-dd HH:mm:ss\';
 71             cfg.fmtType = 2;
 72         }
 73     }
 74     // 显示日期框
 75     let showDateBox = function (datedom)
 76     {
 77         //console.log(datedom);
 78         // 根据日期框的位置显示日期DOM框
 79         let thisleft = inputJQ.offset().left + \'px\';
 80         let thistop = inputJQ.offset().top + inputJQ.outerHeight() + \'px\';
 81         // 576px以下屏(手机屏) 显示在屏幕中央(css媒体查询设为固定定位了)
 82         let ww = $(window).width();
 83         if (ww < 576)
 84         {
 85             thisleft = 0;
 86             thistop = \'25vh\';
 87         }
 88         $(\'.\' + dateboxCls).remove();
 89         // 显示新的日期框
 90         $(\'body\').append(String.DataBind(datedom, { left: thisleft, top: thistop, exportName: exportName }));
 91 
 92         // 576以上屏,input框要能手动输入,焦点在input框.在手机上使用选择,不使用手输,焦点在日期控件上.
 93         if (ww < 576)
 94         {
 95             $(\'.\' + dateboxCls).eq(0).focus();
 96         } else
 97         {
 98 
 99         }
100     }
101     //========================================================//
102     // DOM生成
103     //========================================================//
104     // 生成整个日期框的DOM.并返回
105     let createDom = function ()
106     {
107         let datebox = \'<div class="date-box" style="left:${left};top:${top}" tabindex="-1">{0}{1}{2}{3}</div>\';
108         let ymtarea = String.Format(\'<div class="date-area-ymt">{0}{1}{2}</div>\'
109             , createDom_Year()
110             , createDom_Month()
111             , createDom_Today());
112 
113         let weekarea = String.Format(\'<div class="date-area-week">{0}</div>\'
114             , createDom_Week());
115 
116         let dayarea = String.Format(\'<div class="date-area-day">{0}</div>\'
117             , createDom_Day());
118         // 时间区域
119         let tcarea = \'\';
120         if (cfg.fmtType == 2)
121         {
122             tcarea = String.Format(\'<div class="date-area-tc">{0}{1}{2}</div>\'
123                 , createDom_Time()
124                 , createDom_Clear()
125                 , createDom_Ok());
126         }
127         return String.Format(datebox, ymtarea, weekarea, dayarea, tcarea);
128     }
129 
130     // 1.生成年份区内容 前进,后退,年份 按钮
131     let createDom_Year = function ()
132     {
133         let box = \'<div class="date-area-year">{0}{1}{2}</div>\';
134         let prevbtn = \'<a class="date-btn-prev">&lt;</a>\';
135         let yearbtn = String.Format(
136             \'<b class="date-btn-year" val="{0}">{0}年</b>\'
137             , cfg.year);
138         let nextbtn = \'<a class="date-btn-next">&gt;</a>\';
139         return String.Format(box, prevbtn, yearbtn, nextbtn);
140     }
141 
142     // 1.1生成年份下拉选择框. selectedYear:可指定一个年份为已选定
143     let createDom_YearSelect = function (selectedYear)
144     {
145         let ydoms = \'\';
146         let ylist = domYear_Data();
147         for (let i = 0; i < ylist.length; i++)
148         {
149             ydoms += String.Format(\'<b class="date-option-year {0}" val="{1}">{1}</b>\',
150                 ylist[i] == selectedYear ? "selected" : "", ylist[i]);
151         }
152         return String.Format(\'<div class="date-select-year">{0}</div>\', ydoms);
153     }
154 
155     // 2.生成月份区 前进,后退,月份 按钮
156     let createDom_Month = function ()
157     {
158         let box = \'<div class="date-area-month">{0}{1}{2}</div>\';
159         let prevbtn = \'<a class="date-btn-prev">&lt;</a>\';
160         let monthbtn = String.Format(\'<b class="date-btn-month" val="{0}">{1}月</b>\'
161             , cfg.month, cfg.month + 1);
162         let nextbtn = \'<a class="date-btn-next">&gt;</a>\';
163         return String.Format(box, prevbtn, monthbtn, nextbtn);
164     }
165 
166     // 2.1生成月份下拉选择框. selectedMonth:可指定一个月份为已选定
167     let createDom_MonthSelect = function (selectedMonth)
168     {
169         let mdoms = \'\';
170         for (let i = 0; i < 12; i++)
171         {
172             mdoms += String.Format(
173                 \'<b class="date-option-month {0}" val="{1}">{2}</b>\'
174                 , selectedMonth == i ? "selected" : \'\', i, i + 1);
175         }
176         return String.Format(\'<div class="date-select-month">{0}</div>\', mdoms);
177     }
178 
179     // 3.生成星期标题头
180     let createDom_Week = function ()
181     {
182         let weeksdom = \'\';
183         let weeks = [\'日\', \'一\', \'二\', \'三\', \'四\', \'五\', \'六\'];
184         for (let i = 0; i < weeks.length; i++)
185         {
186             weeksdom += String.Format(\'<b class="date-item-week{0}">{1}</b>\'
187                 , i == 0 || i == 6 ? \' date-item-weekend\' : \'\', weeks[i]);
188         }
189         return weeksdom;
190     }
191 
192     // 4.生成天选项 daylist:日数据.不传则使用选定年月计算出日
193     let createDom_Day = function (daylist)
194     {
195         let data = daylist || domDay_Data();
196         let daydoms = \'\';
197         for (var i = 0; i < data.length; i++)
198         {
199             let json = data[i];
200             let daydom = \'<b class="date-item-day${istoday}${isdayinmonth}${isselected}${isweekend}" year="${yyyy}" month="${MM}" day="${dd}">${dd}</b>\';
201             json.istoday = json.Istoday ? \' date-item-today\' : \'\';
202             json.isselected = json.Isselected ? \' selected\' : \'\';
203             json.isdayinmonth = json.Isdayinmonth ? \'\' : \' date-item-dayoutmonth\';
204             json.isweekend = json.Isweekend ? \' date-item-weekend\' : \'\';
205             json.exportName = exportName;
206             daydoms += String.DataBind(daydom, json);
207         }
208         return daydoms;
209     }
210     // 5.生成时分秒区域
211     let createDom_Time = function ()
212     {
213         let box = \'<div class="date-area-time">{0}{1}{2}</div>\';
214         let hour = String.Format(\'<b class="date-btn-time date-btn-hour">{0}</b>:\', cfg.hour);
215         let minute = String.Format(\'<b class="date-btn-time date-btn-minute">{0}</b>:\',cfg.minute);
216         let second = String.Format(\'<b class="date-btn-time date-btn-second">{0}</b>\', cfg.second);
217         return String.Format(box, hour, minute, second);
218     }
219     // 5.1生成小时选择框
220     let createDom_HourSelect = function ()
221     {
222         let doms = \'\';
223         for (let i = 0; i < 24; i++)
224         {
225             doms += String.Format(
226                 \'<b class="date-option-hour" val="{0}">{0}</b>\', i);
227         }
228         return String.Format(\'<div class="date-select-hour">{0}</div>\', doms);
229     }
230     // 5.2生成分钟,秒钟选择框
231     let createDom_MinuteSelect = function ()
232     {
233         let doms = \'\';
234         for (let i = 0; i < 60; i++)
235         {
236             doms += String.Format(
237                 \'<b class="date-option-minute" val="{0}">{0}</b>\', i);
238         }
239         return String.Format(\'<div class="date-select-minute">{0}</div>\', doms);
240     }
241     // 5.3生成秒钟选择框
242     let createDom_SecondSelect = function ()
243     {
244         let doms = \'\';
245         for (let i = 0; i < 60; i++)
246         {
247             doms += String.Format(\'<b class="date-option-second" val="{0}">{0}</b>\', i);
248         }
249         return String.Format(\'<div class="date-select-second">{0}</div>\', doms);
250     }
251     // 6.生成今天按钮区域
252     let createDom_Today = function ()
253     {
254         return \'<div class="date-area-today"><a class="date-btn-today">今天</a></div>\';
255     }
256     // 7.生成清除按钮区域
257     let createDom_Clear = function ()
258     {
259         let box = \'<div class="date-area-clear">{0}</div>\';
260         return String.Format(box, \'<a class="date-btn-clear">清空</a>\');
261     }
262     // 8.生成确定按钮区域 
263     let createDom_Ok = function ()
264     {
265         let box = \'<div class="date-area-ok">{0}</div>\';
266         return String.Format(box, \'<a class="date-btn-ok">确定</a>\');
267     }
268 
269     // 根据选定的年,月刷新日(用于当在日期框上操作年,月等会改变年月的动作时)
270     // yyyy:指定年,mm:指定月 daysdom:日的父级DOM的JQ对象(.daysrows)
271     let resetDaysDom = function (yyyy, mm)
272     {
273         // 计算出指定年月的日数据
274         let dayslist = domDay_Data(yyyy, mm);
275         // 生成天DOM
276         let daysdom = createDom_Day(dayslist);
277         // 更新天DOM
278         dateboxJQ.find(\'.date-area-day\').html(daysdom);
279         // 事件绑定
280         bindEventForDaySelected();
281     }
282 
283     //=================================================//
284     //    为DOM提供的数据,年份 日
285     //=================================================//
286     // 根据已选年计算年份选项
287     let domYear_Data = function ()
288     {
289         // 年份选择范围固定在[1900-2100]
290         let data = [];
291         for (let i = 1900; i < 2101; i++)
292         {
293             data.push(i);
294         }
295         return data;
296     }
297 
298     // 根据已选年月或者传入指定年月,计算日的起始和结束
299     // 日(天)总共六行七列42个,含已选年月所有日, 前推至最近的周日, 后推至最近或次近的周六
300     let domDay_Data = function (yyyy, mm)
301     {
302         // 指定年 超范围则设为当天年
303         let seledY = $.isNumeric(yyyy) ? parseInt(yyyy) : cfg.year;
304         // 指定月 超范围设为当天月
305         let seledM = $.isNumeric(mm) ? parseInt(mm) : cfg.month;
306 
307         // 指定年月的起止日(1~xx号)
308         let startDay = new Date(seledY, seledM, 1);
309         //let endDay = new Date(seledY, seledM + 1, 0);
310 
311         // 日期起点为指定年月的1号前推到最近的周日,终点为该月最后一天后推到最近的周六
312         startDay.setDate(1 - startDay.getDay());
313         //endDay.setDate(endDay.getDate() + (6 - endDay.getDay()));
314         // 当天日期
315         let todaystr = (new Date()).ToString(\'yyyyMMdd\');
316         let daylist = [];
317         for (let i = 0; i < 42; i++)
318         {
319             let json = {};
320             json.yyyy = startDay.getFullYear();
321             json.MM = startDay.getMonth();
322             json.dd = startDay.getDate();
323             // 日是否属于指定年月中的日
324             json.Isdayinmonth = json.MM == seledM;
325             // 日是否为今天 
326             json.Istoday = startDay.ToString(\'yyyyMMdd\') == todaystr;
327             // 日是否选定(等于文本框中已选日)
328             json.Isselected =
329                 (json.yyyy == cfg.year && json.MM == cfg.month
330                     && json.dd == cfg.day);
331             // 这天是否为周六日(这里未真正判断,而是根据位置判断,每七天为一行,行首周日行尾周六)
332             json.Isweekend = (i % 7 == 0 || (i + 1) % 7 == 0);
333             //
334             startDay.setDate(json.dd + 1);
335             daylist.push(json);
336         }
337         //console.log(daylist);
338         return daylist;
339     }
340 
341     //===============================================================//
342     //    事件方法:年,月的前进后退按钮,年月选择按钮,今天按钮
343     //===============================================================//
344     // 控件显示后,要绑定控件的基础事件.
345     let bindEventForShow = function ()
346     {
347         bindEventForDateBox();
348         bindEventForYearBtn();
349         bindEventForMonthBtn();
350         bindEventForYearMonthPrevNext();
351         bindEventForTodayBtn();
352         bindEventForHourBtn();
353         bindEventForMinBtn();
354         bindEventForSecBtn();
355         bindEventForDaySelected();
356         bindEventForClearBtn();
357         bindEventForOkBtn();
358     }
359 
360     let bindEventForDateBox = function ()
361     {
362         // 点击日期控件以内区域,阻止冒泡到根
363         dateboxJQ.on(\'click\', function (event)
364         {
365             event.stopPropagation();
366             // 点击空白位置时,关闭已经打开的年,月,日,时,分,秒的选择框.需要在子元素上取消冒泡
367             $(this).find(\'[class^=date-select]\').remove();
368         })
369     }
370     let bindEventForYearBtn = function ()
371     {
372         // 点击年按钮 显示年选择框
373         dateboxJQ.find(\'.date-btn-year\').on(\'click\', function (event)
374         {
375             event.stopPropagation();
376             let thisobj = event.currentTarget;
377             //
378             let seledY = $(thisobj).attr(\'val\');
379             // 年份选择框 .date-select-year
380             let yearopsbox = $(thisobj).parent().find(\'.date-select-year\');
381             // 如果已经显示则关闭
382             if (yearopsbox.length == 1)
383             {
384                 yearopsbox.remove(); return;
385             }
386             // 先关闭其它弹出窗
387             dateboxJQ.find(\'[class^=date-select]\').remove();
388             // 生成年份选择框,填充到年份选择框中
389             $(thisobj).parent().append(createDom_YearSelect(seledY));
390             // 定位已选年份到滚动框的中间(视口可见范围内)
391             let yopsbox = $(thisobj).parent().find(\'.date-select-year\');
392             let yseled = yopsbox.find(\'.selected\');
393             if (yseled.length == 0)
394                 yseled = yopsbox.find(\'[val=\' + (new Date()).getFullYear() + \']\');
395             // 计算这个年份选项离父框的TOP值,然后滚动条滚动这个值-父框高/2
396             let scrollval = yseled.position().top - yopsbox.height() / 2;
397             yopsbox.scrollTop(scrollval);
398             // 绑定年份选择点击事件
399             bindEventForYearSelected();
400         })
401     }
402     let bindEventForMonthBtn = function ()

以上是关于JS日期选择器的主要内容,如果未能解决你的问题,请参考以下文章

在片段中创建日期选择器

日期选择器上诉破裂

如何从片段中调用 getSupportFragmentManager()?

软输入键盘隐藏编辑文本

日期选择器如何从下拉列表中选择月份

如何在日期选择器对话框中设置日期限制