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"><</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">></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"><</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">></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日期选择器的主要内容,如果未能解决你的问题,请参考以下文章