使用原生JS實現(xiàn)一個日期選擇器(DatePicker)組件
這是通過原生HTML/CSS/JavaScript完成一個日期選擇器(datepicker)組件,一個純手搓的組件的開發(fā)。主要包括datepicker靜態(tài)結(jié)構(gòu)的編寫、日歷數(shù)據(jù)的計劃獲取、組件的渲染以及組件事件的處理。
根據(jù)調(diào)用時的時間格式參數(shù),可以控制短日期格式或長日期格式。
實現(xiàn)效果(短日期格式)
實現(xiàn)效果(長日期格式)
完整代碼包含4個文件:
- JavaScript實現(xiàn)功能代碼全部在 datepicker-v1.20250113.js 文件
- CSS實現(xiàn)樣式渲染代碼全部在 datepicker-v1.20250113.css 文件
- HTML實現(xiàn)一個調(diào)用demo 在 datepicker.html 文件
- 另外就是 輸入框的小圖標 calendar-icon.png
datepicker-v1.20250113.js 完整代碼如下:
/*! * datepicker Library v1.0 * * Copyright 2025, xiongzaiqiren * Date: Mon Jan 13 2025 11:27:27 GMT+0800 (中國標準時間) */ ; (function () { var datepicker = { paramsDate: function (inputElement, targetFormat) { this.inputElement = inputElement; // 當前輸入框 this.targetFormat = targetFormat || 'yyyy/MM/dd HH:mm:ss'; // 目標日期時間格式 this.monthData = {}; // 繪制日歷組件的數(shù)據(jù)源 this.sureTime = { year: 0, month: 0, date: 0, hour: -1, minute: -1, second: -1 }; // 確定的選中的日期時間,或者初始化到某個時刻,或者是初始化到當前時刻。這里時分秒必需初始化小于0,后米面才好判斷是否要構(gòu)建時分秒控件 }, getMonthDate: function (year, month) { var ret = []; if (!year || !month) { var today = new Date(); year = today.getFullYear(); month = today.getMonth() + 1; } var firstDay = new Date(year, month - 1, 1);//獲取當月第一天 var firstDayWeekDay = firstDay.getDay();//獲取星期幾,才好判斷排在第幾列 if (0 === firstDayWeekDay) {//周日 firstDayWeekDay = 7; } year = firstDay.getFullYear(); month = firstDay.getMonth() + 1; var lastDayOfLastMonth = new Date(year, month - 1, 0);//獲取最后一天 var lastDateOfLastMonth = lastDayOfLastMonth.getDate(); var preMonthDayCount = firstDayWeekDay - 1; var lastDay = new Date(year, month, 0); var lastDate = lastDay.getDate(); for (var i = 0; i < 7 * 6; i++) { var date = i + 1 - preMonthDayCount; var showDate = date; var thisMonth = month; //上一月 if (0 >= date) { thisMonth = month - 1; showDate = lastDateOfLastMonth + date; } else if (date > lastDate) { //下一月 thisMonth = month + 1; showDate = showDate - lastDate; } if (0 === thisMonth) { thisMonth = 12; } if (13 === thisMonth) { thisMonth = 1; } ret.push({ month: thisMonth, date: date, showDate: showDate }) } return { year: year, month: month, days: ret }; } }; window.datepicker = datepicker;//該函數(shù)唯一暴露的對象 })(); /***** main.js *****/ (function () { var datepicker = window.datepicker; var $datepicker_wrapper; //渲染函數(shù),由于沒有使用第三方插件或庫,所以使用的是模板拼接的方法 datepicker.buildUi = function (monthData, sureTime) { // monthData = datepicker.getMonthDate(year, month); // year, month, monthData 是面板需要繪畫的日期時間集合 var html = '<div class="ui-datepicker-header">' + '<a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="ui-datepicker-btn ui-datepicker-prevYear-btn">≪</a>' + '<a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="ui-datepicker-btn ui-datepicker-prev-btn"><</a>' + '<a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="ui-datepicker-btn ui-datepicker-nextYear-btn">≫</a>' + '<a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="ui-datepicker-btn ui-datepicker-next-btn">></a>' + '<span class="datepicker-curr-month">' + monthData.year + '-' + monthData.month + '</span>' + '</div>' + '<div class="ui-datepicker-body">' + '<table>' + '<thead>' + '<tr>' + '<th>\u4e00</th>' + '<th>\u4e8c</th>' + '<th>\u4e09</th>' + '<th>\u56db</th>' + '<th>\u4e94</th>' + '<th>\u516d</th>' + '<th>\u65e5</th>' + '</tr>' + '</thead>' + '<tbody>'; function coreMonth(coreMonth, month) { return coreMonth == month; } function isToday(year, month, date) { const _today = new Date().getFullYear() + '/' + (new Date().getMonth() + 1) + '/' + new Date().getDate(); return (year + '/' + month + '/' + date) == _today; } function sureTimeIsToday(year, month, date, sureTime) { return (!!sureTime && (sureTime.year === year && sureTime.month === month && sureTime.date === date)); } for (var i = 0; i < monthData.days.length; i++) { var date = monthData.days[i]; if (i % 7 === 0) { html += '<tr>'; } html += '<td class="' + ((i % 7 === 5 || i % 7 === 6) ? 'weekend' : '') + (coreMonth(monthData.month, date.month) ? '' : ' notmonth') + (isToday(monthData.year, date.month, date.showDate) ? ' today' : '') + (sureTimeIsToday(monthData.year, date.month, date.showDate, sureTime) ? ' active' : '') + '" data-date="' + date.date + '">' + date.showDate + '</td>'; if (i % 7 === 6) { html += '</tr>'; } } html += '</tbody>' + '</table>' + '</div>'; function buildTimeOptions(max) { let _s = ''; for (i = 0; i <= max; i++) { let _n = i < 10 ? ('0' + i) : i; _s += '<option value="' + _n + '">' + _n + '</option>'; } return _s; } html += '<div class="ui-datepicker-footer">' + '<a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="ui-datepicker-btn ui-datepicker-today-btn">\u4eca\u5929</a>'; if (!!sureTime && (0 <= sureTime.hour && 0 <= sureTime.minute && 0 <= sureTime.second)) { html += '<select class="hour">' + buildTimeOptions(23) + '</select>' + '<select class="minute">' + buildTimeOptions(59) + '</select>' + '<select class="second">' + buildTimeOptions(59) + '</select>'; } html += '<a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="ui-datepicker-btn ui-datepicker-ok-btn">\u786e\u5b9a</a>' + '</div>'; return html; }; //日歷渲染函數(shù) datepicker.render = function (paramsDate, direction) { var year, month; if (!!paramsDate.monthData && 0 < paramsDate.monthData.year) { year = paramsDate.monthData.year; month = paramsDate.monthData.month; } else if (!!paramsDate.sureTime && (0 < paramsDate.sureTime.year && 0 < paramsDate.sureTime.month)) { // 根據(jù)輸入框的值初始化確定日期 year = paramsDate.sureTime.year; month = paramsDate.sureTime.month; } if ('prev' === direction) { month--; if (month < 1) { year--; month = 12; } } else if ('next' === direction) { month++; } else if ('prevYear' === direction) { year--; } else if ('nextYear' === direction) { year++; } else if ('today' === direction) { let t = new Date(); year = t.getFullYear(); month = t.getMonth() + 1; paramsDate.sureTime.year = year; paramsDate.sureTime.month = month; paramsDate.sureTime.date = t.getDate(); if (0 <= paramsDate.sureTime.hour && 0 <= paramsDate.sureTime.minute && 0 <= paramsDate.sureTime.second) { paramsDate.sureTime.hour = t.getHours(); paramsDate.sureTime.minute = t.getMinutes(); paramsDate.sureTime.second = t.getSeconds(); } } paramsDate.monthData = datepicker.getMonthDate(year, month); // year, month, monthData 是面板需要繪畫的日期時間集合 var html = datepicker.buildUi(paramsDate.monthData, paramsDate.sureTime); $datepicker_wrapper = document.querySelector('.ui-datepicker-wrapper'); if (!$datepicker_wrapper) { $datepicker_wrapper = document.createElement('div'); $datepicker_wrapper.className = 'ui-datepicker-wrapper'; } $datepicker_wrapper.innerHTML = html; document.body.appendChild($datepicker_wrapper); // 繪畫完了,初始化選中時間 if (!!paramsDate.sureTime && (0 <= paramsDate.sureTime.hour && 0 <= paramsDate.sureTime.minute && 0 <= paramsDate.sureTime.second)) { setSelectedByValue('.ui-datepicker-wrapper select.hour', paramsDate.sureTime.hour); setSelectedByValue('.ui-datepicker-wrapper select.minute', paramsDate.sureTime.minute); setSelectedByValue('.ui-datepicker-wrapper select.second', paramsDate.sureTime.second); } }; //初始換函數(shù) datepicker.main = function (paramsDate) { var $targetFormat = paramsDate.targetFormat; var $input = paramsDate.inputElement; // 根據(jù)輸入框的值初始化控件的值 let initInputdate = new Date($input.value); if (!!initInputdate && initInputdate.getFullYear() > 0) { paramsDate.sureTime.year = initInputdate.getFullYear(); paramsDate.sureTime.month = initInputdate.getMonth() + 1; paramsDate.sureTime.date = initInputdate.getDate(); if (/([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])/gi.test($input.value)) { // 校驗時分秒:格式:HH:mm:ss paramsDate.sureTime.hour = initInputdate.getHours(); paramsDate.sureTime.minute = initInputdate.getMinutes(); paramsDate.sureTime.second = initInputdate.getSeconds(); } } if (0 > paramsDate.sureTime.hour || 0 > paramsDate.sureTime.minute || 0 > paramsDate.sureTime.second) { if (!!$targetFormat && ($targetFormat.toLocaleLowerCase().includes('hh:mm:ss'))) { // 將展示時分秒控件 paramsDate.sureTime.hour = 0; paramsDate.sureTime.minute = 0; paramsDate.sureTime.second = 0; } else { // 不展示時分秒控件 paramsDate.sureTime.hour = -1; paramsDate.sureTime.minute = -1; paramsDate.sureTime.second = -1; } } // 在初始化控件之前,清理掉以前的日期時間控件 const divsToRemove = document.querySelectorAll('.ui-datepicker-wrapper'); divsToRemove.forEach(div => div.remove()); datepicker.render(paramsDate); var isOpen = false; if (isOpen) { $datepicker_wrapper.classList.remove('ui-datepicker-wrapper-show'); isOpen = false; } else { $datepicker_wrapper.classList.add('ui-datepicker-wrapper-show'); var left = $input.offsetLeft; var top = $input.offsetTop; var height = $input.offsetHeight; $datepicker_wrapper.style.top = top + height + 2 + 'px'; $datepicker_wrapper.style.left = left + 'px'; isOpen = true; } //給按鈕添加點擊事件 datepicker.addEventListener($datepicker_wrapper, 'click', function (e) { var $target = e.target; //上一月,下一月 if ($target.classList.contains('ui-datepicker-prev-btn')) { datepicker.render(paramsDate, 'prev'); } else if ($target.classList.contains('ui-datepicker-next-btn')) { datepicker.render(paramsDate, 'next'); } //上一年,下一年 else if ($target.classList.contains('ui-datepicker-prevYear-btn')) { datepicker.render(paramsDate, 'prevYear'); } else if ($target.classList.contains('ui-datepicker-nextYear-btn')) { datepicker.render(paramsDate, 'nextYear'); } //今天 else if ($target.classList.contains('ui-datepicker-today-btn')) { datepicker.render(paramsDate, 'today'); } if ($target.tagName.toLocaleLowerCase() === 'td') { // 移除其他日期選中狀態(tài) document.querySelectorAll('.ui-datepicker-wrapper td').forEach(function (td) { if (td.classList.contains('active')) { td.classList.remove('active'); } }); // 通過classname 設(shè)置選中日期 $target.classList.add('active'); } if (!$target.classList.contains('ui-datepicker-ok-btn')) { return true; } // 以下是點擊“確定”之后的代碼 var selected_date; var selectedTd = document.querySelector('.ui-datepicker-wrapper td.active'); if (!!selectedTd) { selected_date = selectedTd.dataset.date || 0; } if (3 === document.querySelectorAll('.ui-datepicker-wrapper select').length) { var selectElementHour = document.querySelector('.ui-datepicker-wrapper select.hour'); paramsDate.sureTime.hour = selectElementHour.options[selectElementHour.selectedIndex].value || 0; var selectElementMinute = document.querySelector('.ui-datepicker-wrapper select.minute'); paramsDate.sureTime.minute = selectElementMinute.options[selectElementMinute.selectedIndex].value || 0; var selectElementSecond = document.querySelector('.ui-datepicker-wrapper select.second'); paramsDate.sureTime.second = selectElementSecond.options[selectElementSecond.selectedIndex].value || 0; } if (1 <= selected_date && selected_date <= 31) { // 至少選中到天 let date; if (0 <= paramsDate.sureTime.hour) { date = new Date(paramsDate.monthData.year, paramsDate.monthData.month - 1, selected_date, paramsDate.sureTime.hour, paramsDate.sureTime.minute, paramsDate.sureTime.second); } else { date = new Date(paramsDate.monthData.year, paramsDate.monthData.month - 1, selected_date); } $input.value = dateFormat(date, $targetFormat); } $datepicker_wrapper.classList.remove('ui-datepicker-wrapper-show'); isOpen = false; }, false); }; /* 定義一個函數(shù),用于添加事件監(jiān)聽器,現(xiàn)代瀏覽器還是舊版IE瀏覽器。 */ datepicker.addEventListener = function (el, eventName, callback, useCapture) { if (el.addEventListener) { el.addEventListener(eventName, callback, useCapture); } else if (el.attachEvent) { el.attachEvent('on' + eventName, callback); } }; // 給輸入框綁定點擊事件 datepicker.init = function (input, targetFormat) { this.addEventListener(document.querySelector(input), 'click', function (e) { let $paramsDate = new datepicker.paramsDate(e.target, targetFormat); datepicker.main($paramsDate); }); }; // 通過value設(shè)置選中項 function setSelectedByValue(selectors, value) { var select = document.querySelector(selectors); if (!!!select || !!!select.options) { return false; } for (var i = 0; i < select.options.length; i++) { if (parseInt(select.options[i].value) == value) { select.options[i].selected = true; break; } } }; /* 日期時間格式化·開始 */ Date.prototype.Format = function (fmt) { if (!this || this.getFullYear() <= 1) return ''; var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours() == 0 ? 12 : this.getHours(), //小時 "H+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "f": this.getMilliseconds() //毫秒 }; var week = { "0": "\u65e5", "1": "\u4e00", "2": "\u4e8c", "3": "\u4e09", "4": "\u56db", "5": "\u4e94", "6": "\u516d" }; const reg_y = /(y+)/; if (reg_y.test(fmt)) { fmt = fmt.replace(reg_y.exec(fmt)[1], (this.getFullYear() + "").slice(4 - reg_y.exec(fmt)[1].length)); } const reg_E = /(E+)/; if (reg_E.test(fmt)) { fmt = fmt.replace(reg_E.exec(fmt)[1], ((reg_E.exec(fmt)[1].length > 1) ? (reg_E.exec(fmt)[1].length > 2 ? "\u661f\u671f" : "\u5468") : "") + week[this.getDay() + ""]); } for (var k in o) { const reg_k = new RegExp("(" + k + ")"); if (reg_k.test(fmt)) { const t = reg_k.exec(fmt)[1]; fmt = fmt.replace(t, (t.length == 1) ? (o[k]) : (("00" + o[k]).slice(("" + o[k]).length))); } } return fmt; }; function dateFormat(date, format) { if (!date) return ''; if (!format) format = 'yyyy/MM/dd HH:mm:ss'; if ("object" == typeof (date)) return date.Format(format); else { return (new Date(date)).Format(format); } }; /* 日期時間格式化·結(jié)束 */ })();
datepicker-v1.20250113.css 完整代碼如下:
.datepicker { width: 230px; padding: 5px; line-height: 24px; background: url(calendar-icon.png); background-repeat: no-repeat; background-position: right 3px center; padding-right: 20px; border: 1px solid #ccc; border-radius: 4px; -o-border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; } .datepicker:focus { outline: none; border: 1px solid #1abc9c; } /* 最外層區(qū)域 */ .ui-datepicker-wrapper { display: none; /*添加默認隱藏*/ position: absolute; /*添加絕對定位*/ width: 240px; font-size: 16px; color: #666666; background-color: #fff; box-shadow: 2px 2px 8px 2px rgba(128, 128, 128, 0.3); } /* 頭部區(qū)域 */ .ui-datepicker-wrapper .ui-datepicker-header,.ui-datepicker-wrapper .ui-datepicker-footer { padding: 0 20px; height: 50px; line-height: 50px; text-align: center; background: #f0f0f0; border-bottom: 1px solid #cccccc; font-weight: bold; } /* 設(shè)置兩個按鈕 */ .ui-datepicker-wrapper .ui-datepicker-btn { font-family: serif; font-size: 20px; width: 20px; height: 50px; line-height: 50px; color: #1abc9c; text-align: center; cursor: pointer; text-decoration: none; } .ui-datepicker-wrapper .ui-datepicker-prev-btn,.ui-datepicker-wrapper .ui-datepicker-prevYear-btn { float: left; } .ui-datepicker-wrapper .ui-datepicker-next-btn,.ui-datepicker-wrapper .ui-datepicker-nextYear-btn { float: right; } .ui-datepicker-wrapper .ui-datepicker-footer{ display: flex; line-height: 30px; height: 30px; padding: 1px 1px; background-color: #fff; } .ui-datepicker-wrapper .ui-datepicker-footer a,.ui-datepicker-wrapper select,.ui-datepicker-wrapper select option{ flex: 1 1 auto; width: 100%; height: 30px; line-height: 30px; font-size: 12px; text-align: center; cursor: pointer; } .ui-datepicker-wrapper .ui-datepicker-footer .ui-datepicker-btn{ height: 28px; line-height: 28px; border:1px solid #c0c0c0; } .ui-datepicker-wrapper .ui-datepicker-footer .ui-datepicker-btn:hover,.ui-datepicker-wrapper .ui-datepicker-footer .ui-datepicker-btn:active{ border:1px solid #1abc9c; } /* body區(qū)域 */ .ui-datepicker-wrapper .ui-datepicker-body table { width: 100%; border-collapse: separate; border-spacing: 1px; } /* 表頭和正文 */ .ui-datepicker-wrapper .ui-datepicker-body th, .ui-datepicker-wrapper .ui-datepicker-body td { height: 30px; text-align: center; } .ui-datepicker-wrapper .ui-datepicker-body th { font-size: 14px; height: 40px; line-height: 40px; } /* 表格部分 */ .ui-datepicker-wrapper .ui-datepicker-body td { border: 1px solid #f0f0f0; font-size: 12px; width: 14%; cursor: pointer; } /* “周末”的日期 */ .ui-datepicker-wrapper .ui-datepicker-body td.weekend{ color: #FF5722; } /* 非“當前展示月份”的日期 */ .ui-datepicker-wrapper .ui-datepicker-body td.notmonth{ background-color: #f3f3f3; } /* “今天”的日期 */ .ui-datepicker-wrapper .ui-datepicker-body td.today { border-color:#7cffe5; } .ui-datepicker-wrapper .ui-datepicker-body td.today:hover,.ui-datepicker-wrapper .ui-datepicker-body td.today:active, .ui-datepicker-wrapper .ui-datepicker-body td:hover,.ui-datepicker-wrapper .ui-datepicker-body td:focus { border-color: #c0c0c0; } /* 選中的日期 */ .ui-datepicker-wrapper .ui-datepicker-body td:active,.ui-datepicker-wrapper .ui-datepicker-body td.active { border-color: #1abc9c; } .ui-datepicker-wrapper-show { display: block; }
datepicker.html 做的一個簡易 demo 完整代碼如下:
<!doctype html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="MobileOptimized" content="240"> <meta name="applicable-device" content="mobile"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta name="format-detection" content="telephone=no,email=no,adress=no"> <link href="datepicker-v1.20250113.css" rel="external nofollow" rel="external nofollow" rel="stylesheet" type="text/css"> <title>測試demo -datepicker </title> <script src="datepicker-v1.20250113.js"></script> </head> <body> 支持兩種格式的日期時間輸入控件。短日期時間格式的: <input type="text" class="datepicker" id="t1"> 長日期時間格式的: <input type="text" class="datepicker" id="t2"> <!-- JS腳本 --> <script> // var monthDate = datepicker.getMonthDate(2019, 2); // console.log(monthDate); // datepicker.init(document.querySelector('.ui-datepicker-wrapper')) document.querySelector('#t1').value = '2008/08/20'; datepicker.init('#t1', 'yyyy/MM/dd'); datepicker.init('#t2', 'yyyy/MM/dd HH:mm:ss'); </script> 調(diào)用示例: <code> <pre style="background-color: #eee;padding: 5px 3px;border: 1px solid #ccc;"> <!-- 引入 日期時間控件樣式表文件 --> <link href="datepicker-v1.20250113.css" rel="external nofollow" rel="external nofollow" rel="stylesheet" type="text/css"> <!-- 引入 日期時間控件 JavaScript文件 --> <script src="datepicker-v1.20250113.js"></script> 支持兩種格式的日期時間輸入控件。短日期時間格式的: <input type="text" class="datepicker" id="t1"> 長日期時間格式的: <input type="text" class="datepicker" id="t2"> <script> document.querySelector('#t1').value = '2008/08/20'; datepicker.init('#t1', 'yyyy/MM/dd'); datepicker.init('#t2', 'yyyy/MM/dd HH:mm:ss'); </script> </pre> </code> </body> </html>
最后是輸入框里的小圖標:
以上就是使用原生JS實現(xiàn)一個日期選擇器(DatePicker)組件的詳細內(nèi)容,更多關(guān)于JS實現(xiàn)日期選擇器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信實現(xiàn)自動跳轉(zhuǎn)到用其他瀏覽器打開指定APP下載
這篇文章主要介紹了微信實現(xiàn)自動跳轉(zhuǎn)到用其他瀏覽器打開指定APP下載,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02JavaScript使用簡單正則表達式的數(shù)據(jù)驗證功能示例
這篇文章主要介紹了JavaScript使用簡單正則表達式的數(shù)據(jù)驗證功能,結(jié)合實例形式分析了JS針對表單輸入內(nèi)容的簡單正則驗證操作技巧,需要的朋友可以參考下2017-01-01