浏览器自带
浏览器自带日期控件,使用<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 () 403 { 404 // 点击月按钮 显示月选择框 405 dateboxJQ.find('.date-btn-month').on('click', function (event) 406 { 407 event.stopPropagation(); 408 let thisobj = event.currentTarget; 409 // 410 let seledM = $(thisobj).attr('val'); 411 let monthsops = $(thisobj).parent().find('.date-select-month'); 412 // 如果已经显示则关闭 413 if (monthsops.length == 1) 414 { 415 monthsops.remove(); return; 416 } 417 // 先关闭其它弹出窗 418 dateboxJQ.find('[class^=date-select]').remove(); 419 $(thisobj).parent().append(createDom_MonthSelect(seledM)); 420 // 绑定月分选项点击事件 421 bindEventForMonthSelected(); 422 }) 423 } 424 let bindEventForYearSelected = function () 425 { 426 // 点击年份选项 选定一个年份 427 dateboxJQ.find('.date-option-year').on('click', function (event) 428 { 429 event.stopPropagation(); 430 let thisobj = event.currentTarget; 431 // 432 // 所选年份值 433 let y = $(thisobj).attr('val'); 434 // 更新年份按钮显示值 435 dateboxJQ.find('.date-btn-year').attr('val', y).html(y + '年'); 436 // 关闭年份选择框 437 $(thisobj).parent().remove(); 438 // 刷新 日 439 let m = dateboxJQ.find('.date-btn-month').attr('val'); 440 resetDaysDom(y, m); 441 }) 442 } 443 let bindEventForMonthSelected = function () 444 { 445 // 点击月份选项 选定一个月份 446 dateboxJQ.find('.date-option-month').on('click', function (event) 447 { 448 event.stopPropagation(); 449 let thisobj = event.currentTarget; 450 // 451 // 所选月份值 452 let m = parseInt($(thisobj).attr('val')); 453 dateboxJQ.find('.date-btn-month').attr('val', m).html((m + 1) + '月'); 454 // 关闭月份选择框 455 $(thisobj).parent().remove(); 456 // 刷新 日 457 let y = dateboxJQ.find('.date-btn-year').attr('val'); 458 resetDaysDom(y, m); 459 }) 460 } 461 let bindEventForYearMonthPrevNext = function () 462 { 463 // 点击年份,月份的前进和后退按钮 btntype:1=年按钮,2=月按钮. dir:1=前进,2=后退 464 dateboxJQ.find('.date-btn-prev,.date-btn-next').on('click', function (event) 465 { 466 event.stopPropagation(); 467 let thisobj = event.currentTarget; 468 // 469 let btntype = $(thisobj).parent().hasClass('date-area-year') ? 1 : 2; 470 let dir = $(thisobj).hasClass('date-btn-next') ? 1 : 2; 471 // 472 let ybtn = dateboxJQ.find('.date-btn-year'); 473 let mbtn = dateboxJQ.find('.date-btn-month'); 474 let y = parseInt(ybtn.attr('val')); 475 let m = parseInt(mbtn.attr('val')); 476 // 计算并刷新年或月按钮值 年份前进后退值[1-9999] 477 if (btntype == 1) 478 { 479 y = dir == 1 ? y + 1 : y - 1; 480 if (y < 1) y = 9999; 481 else if (y > 9999) y = 1; 482 } 483 else if (btntype == 2) 484 { 485 m = dir == 1 ? m + 1 : m - 1; 486 if (m < 0) 487 { 488 m = 11; 489 // 年往后退一年,如果为1年,则不变 490 if (y > 1) 491 y = y - 1; 492 } 493 else if (m > 11) 494 { 495 m = 0; 496 // 年往前进一年,如果为9999年,则不变 497 if (y < 9999) 498 y = y + 1; 499 } 500 } 501 ybtn.attr('val', y).html(y + '年'); 502 mbtn.attr('val', m).html((m + 1) + '月'); 503 // 刷新日 504 //console.log(y+'----'+m); 505 resetDaysDom(y, m); 506 }) 507 } 508 let bindEventForTodayBtn = function () 509 { 510 // 点击今天按钮 设置今天日期到input框 511 dateboxJQ.find('.date-btn-today').on('click', function (event) 512 { 513 event.stopPropagation(); 514 let thisobj = event.currentTarget; 515 // 516 let today = new Date(new Date().toLocaleDateString()); 517 inputJQ.val(today.ToString(cfg.dateFmt)); 518 // 519 mydate.close(); 520 }) 521 } 522 let bindEventForHourBtn = function () 523 { 524 // 点击小时按钮 显示小时选择框 525 dateboxJQ.find('.date-btn-hour').on('click', function (event) 526 { 527 event.stopPropagation(); 528 let thisobj = event.currentTarget; 529 // 530 let hourselecct = $(thisobj).parent().find('.date-select-hour'); 531 // 点击时分秒下拉框按钮时,先取消其按钮的打开样式,打开后,再给自己加上打开样式 532 $(thisobj).parent().find('.date-btn-time').removeClass('open'); 533 // 如果已经显示则关闭 534 if (hourselecct.length == 1) 535 { 536 hourselecct.remove(); return; 537 } 538 // 先关闭其它弹出窗 539 dateboxJQ.find('[class^=date-select]').remove(); 540 $(thisobj).parent().append(createDom_HourSelect()); 541 $(thisobj).addClass('open'); 542 // 绑定小时选项点击事件 543 bindEventForHourSelected(); 544 }) 545 } 546 let bindEventForMinBtn = function () 547 { 548 // 点击分钟按钮 显示分钟选择框 549 dateboxJQ.find('.date-btn-minute').on('click', function (event) 550 { 551 event.stopPropagation(); 552 let thisobj = event.currentTarget; 553 // 554 let minselecct = $(thisobj).parent().find('.date-select-minute'); 555 // 点击时分秒下拉框按钮时,先取消其按钮的打开样式,打开后,再给自己加上打开样式 556 $(thisobj).parent().find('.date-btn-time').removeClass('open'); 557 // 如果已经显示则关闭 558 if (minselecct.length == 1) 559 { 560 minselecct.remove(); return; 561 } 562 // 先关闭其它弹出窗 563 dateboxJQ.find('[class^=date-select]').remove(); 564 $(thisobj).parent().append(createDom_MinuteSelect()); 565 $(thisobj).addClass('open'); 566 // 绑定分钟选项点击事件 567 bindEventForMinSelected(); 568 }) 569 } 570 let bindEventForSecBtn = function () 571 { 572 // 点击秒钟按钮 显示秒钟选择框 573 dateboxJQ.find('.date-btn-second').on('click', function (event) 574 { 575 event.stopPropagation(); 576 let thisobj = event.currentTarget; 577 // 578 let secselecct = $(thisobj).parent().find('.date-select-second'); 579 // 点击时分秒下拉框按钮时,先取消其按钮的打开样式,打开后,再给自己加上打开样式 580 $(thisobj).parent().find('.date-btn-time').removeClass('open'); 581 // 如果已经显示则关闭 582 if (secselecct.length == 1) 583 { 584 secselecct.remove(); return; 585 } 586 // 先关闭其它弹出窗 587 dateboxJQ.find('[class^=date-select]').remove(); 588 $(thisobj).parent().append(createDom_SecondSelect()); 589 $(thisobj).addClass('open'); 590 // 绑定秒钟选项点击事件 591 bindEventForSecSelected(); 592 }) 593 } 594 let bindEventForHourSelected = function () 595 { 596 // 选择小时 修改小时按钮显示值 597 dateboxJQ.find('.date-option-hour').on('click', function (event) 598 { 599 event.stopPropagation(); 600 let thisobj = event.currentTarget; 601 // 602 let h = $(thisobj).attr('val'); 603 dateboxJQ.find('.date-btn-hour').html(h); 604 cfg.hour = h; 605 // 606 $(thisobj).parent().remove(); 607 }) 608 } 609 let bindEventForMinSelected = function () 610 { 611 // 选择分钟 修改按钮显示值 612 dateboxJQ.find('.date-option-minute').on('click', function (event) 613 { 614 event.stopPropagation(); 615 let thisobj = event.currentTarget; 616 // 617 let m = $(thisobj).attr('val'); 618 dateboxJQ.find('.date-btn-minute').html(m); 619 cfg.minute = m; 620 // 621 $(thisobj).parent().remove(); 622 }) 623 } 624 let bindEventForSecSelected = function () 625 { 626 // 选择秒钟 修改按钮显示值 627 dateboxJQ.find('.date-option-second').on('click', function (event) 628 { 629 event.stopPropagation(); 630 let thisobj = event.currentTarget; 631 // 632 let s = $(thisobj).attr('val'); 633 dateboxJQ.find('.date-btn-second').html(s); 634 cfg.second = s; 635 // 636 $(thisobj).parent().remove(); 637 }) 638 } 639 let bindEventForDaySelected = function () 640 { 641 // 选择天 设置这天日期到Input框 642 dateboxJQ.find('.date-item-day').on('click', function (event) 643 { 644 event.stopPropagation(); 645 let thisobj = event.currentTarget; 646 // 647 let date = new Date($(thisobj).attr('year'), $(thisobj).attr('month') 648 , $(thisobj).attr('day'),cfg.hour,cfg.minute,cfg.second); 649 inputJQ.val(date.ToString(cfg.dateFmt)); 650 // 651 mydate.close(); 652 }) 653 } 654 let bindEventForClearBtn = function () 655 { 656 // 点击清空 657 dateboxJQ.find('.date-btn-clear').on('click', function (event) 658 { 659 event.stopPropagation(); 660 let thisobj = event.currentTarget; 661 // 662 inputJQ.val(''); 663 mydate.close(); 664 }) 665 } 666 let bindEventForOkBtn = function () 667 { 668 // 点击确定按钮 669 dateboxJQ.find('.date-btn-ok').on('click', function (event) 670 { 671 event.stopPropagation(); 672 let thisobj = event.currentTarget; 673 // 674 // 找到选中的日 设置到Input框 如果没有选中的日,使用今天 675 let seledDay = dateboxJQ.find('.date-item-day.selected'); 676 let inputVal = seledDay.length == 0 677 ? new Date(new Date().toLocaleDateString(), cfg.hour, cfg.minute, cfg.second) 678 : new Date(seledDay.attr('year'), seledDay.attr('month'), seledDay.attr('day') 679 , cfg.hour, cfg.minute, cfg.second); 680 681 inputJQ.val(inputVal.ToString(cfg.dateFmt)); 682 // 683 mydate.close(); 684 }) 685 } 686 687 // 关闭日期框 688 mydate.close = function () 689 { 690 dateboxJQ = null; 691 inputJQ = null; 692 cfg = null; 693 $('.' + dateboxCls).remove(); 694 } 695 696 // 点击日期控件以外区域,关闭控件. 697 $(document).click(function () 698 { 699 mydate.close(); 700 }) 701 // 702 window[exportName] = mydate; 703 })();
css

1 .date-box { 2 position: absolute; 3 width: 308px; 4 cursor: default; 5 border: 1px solid #e9ecef; 6 padding: 10px 5px; 7 text-align: center; 8 box-shadow: 1px 1px 10px #dee2e6; 9 background-color: #fff; 10 outline: none; 11 -webkit-user-select: none; 12 -moz-user-select: none; 13 -ms-user-select: none; 14 user-select: none; } 15 16 .date-area-ymt { 17 display: flex; } 18 19 .date-area-year, .date-area-month, .date-area-today { 20 position: relative; 21 padding: 0 4px; } 22 23 .date-area-year { 24 flex: 0 1 44%; } 25 26 .date-area-month { 27 flex: 0 1 38%; } 28 29 .date-area-today { 30 flex: 0 1 18%; } 31 32 .date-area-week { 33 display: flex; 34 padding: 5px 0; } 35 36 .date-area-day { 37 display: flex; 38 flex-wrap: wrap; } 39 40 .date-area-tc { 41 display: flex; 42 position: relative; } 43 44 .date-area-time, .date-area-clear, .date-area-ok { 45 border-top: 1px solid #e9ecef; 46 padding-top: 10px; } 47 48 .date-area-time { 49 flex: 0 1 64%; } 50 51 .date-area-clear, .date-area-ok { 52 flex: 0 1 18%; 53 padding-left: 4px; 54 padding-right: 4px; } 55 56 .date-select-year, .date-select-month, .date-select-hour, .date-select-minute, .date-select-second { 57 display: flex; 58 flex-wrap: wrap; 59 position: absolute; 60 left: 0; 61 border: 1px solid #dee2e6; 62 border-radius: 4px; 63 border-top: none; 64 background-color: #f8f9fa; 65 z-index: 9999; } 66 67 .date-select-month { 68 width: 100%; } 69 70 .date-select-year { 71 width: 200px; 72 height: 180px; 73 overflow-x: hidden; 74 overflow-y: scroll; 75 padding: 10px 0; } 76 77 .date-select-hour, .date-select-minute, .date-select-second { 78 width: 100%; 79 bottom: 28px; } 80 81 .date-option-year, .date-option-month, .date-option-hour, .date-option-minute, .date-option-second { 82 border-bottom: 1px solid #dee2e6; 83 height: 28px; 84 line-height: 28px; 85 font-weight: 500; } 86 87 .date-option-year, .date-option-month { 88 flex: 0 1 33.33333333%; } 89 90 .date-option-year:hover, .date-option-month:hover, .date-option-hour:hover, .date-option-minute:hover { 91 background-color: #dee2e6; } 92 93 .date-option-year.selected, .date-option-month.selected { 94 background-color: #007bff; } 95 96 .date-option-hour { 97 flex: 0 1 16.66666666%; } 98 99 .date-option-minute, .date-option-second { 100 flex: 0 1 10%; } 101 102 .date-btn-year, .date-btn-month, .date-btn-today, .date-btn-prev, .date-btn-next, .date-btn-time, .date-btn-clear, .date-btn-ok { 103 display: inline-block; 104 font-weight: 600; 105 border: 1px solid #e9ecef; 106 border-radius: 3px; 107 height: 26px; 108 line-height: 26px; 109 cursor: pointer; } 110 111 .date-btn-year, .date-btn-month { 112 color: #6c757d; 113 width: calc(100% - 52px); } 114 115 .date-btn-today, .date-btn-prev, .date-btn-next { 116 color: #007bff; } 117 118 .date-btn-today, .date-btn-clear, .date-btn-ok { 119 width: 100%; } 120 121 .date-btn-clear, .date-btn-ok { 122 font-weight: 500; 123 color: #6c757d; } 124 125 .date-btn-prev, .date-btn-next { 126 width: 26px; } 127 128 .date-btn-time { 129 font-weight: 500; 130 width: 32px; 131 margin: 0 5px; } 132 133 .date-btn-time.open { 134 color: #fff; 135 background-color: #007bff; } 136 137 .date-item-week, .date-item-day { 138 flex: 0 1 14.28571428%; 139 font-weight: 500; 140 height: 28px; 141 line-height: 28px; 142 border-radius: 3px; } 143 144 .date-item-week { 145 font-weight: 600; } 146 147 .date-item-weekend { 148 color: #dc3545; } 149 150 .date-item-day:hover { 151 background-color: #e9ecef; } 152 153 .date-item-dayoutmonth { 154 opacity: .3; } 155 156 .date-item-day.selected:not(.date-item-today) { 157 background-color: #dee2e6; } 158 159 .date-item-today { 160 color: #fff; 161 background-color: #007bff; } 162 163 @media only screen and (max-width: 576px) { 164 .date-box { 165 position: fixed; 166 width: calc(100vw - 2px); 167 height: 312px; 168 border-color: #007bff; 169 right: 0; 170 margin: auto; } 171 .date-btn-year, .date-btn-month, .date-btn-today, .date-btn-prev, .date-btn-next, .date-btn-time, .date-btn-clear, .date-btn-ok { 172 height: 36px; 173 line-height: 36px; } 174 .date-btn-year, .date-btn-month { 175 width: calc(100% - 72px); } 176 .date-btn-prev, .date-btn-next { 177 width: 36px; } 178 .date-option-year, .date-option-month, .date-option-hour, .date-option-minute, .date-option-second { 179 height: 36px; 180 line-height: 36px; } 181 .date-select-hour, .date-select-minute, .date-select-second { 182 bottom: 38px; } }
JS原生版本