这是通过原生html/css/javascript完成一个日期选择器(datepicker)组件,一个纯手搓的组件的开发。主要包括datepicker静态结构的编写、日历数据的计划获取、组件的渲染以及组件事件的处理。
根据调用时的时间格式参数,可以控制短日期格式或长日期格式。
实现效果(短日期格式)

实现效果(长日期格式)

完整代码包含4个文件:
- javascript实现功能代码全部在 datepicker-v1.20250113.js 文件
- css实现样式渲染代码全部在 datepicker-v1.20250113.css 文件
- html实现一个调用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 = {}; // 绘制日历组件的数据源
this.suretime = { year: 0, month: 0, date: 0, hour: -1, minute: -1, second: -1 }; // 确定的选中的日期时间,或者初始化到某个时刻,或者是初始化到当前时刻。这里时分秒必需初始化小于0,后米面才好判断是否要构建时分秒控件
},
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;//该函数唯一暴露的对象
})();
/***** main.js *****/
(function () {
var datepicker = window.datepicker;
var $datepicker_wrapper;
//渲染函数,由于没有使用第三方插件或库,所以使用的是模板拼接的方法
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;
};
//日历渲染函数
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)) {
// 根据输入框的值初始化确定日期
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);
}
};
//初始换函数
datepicker.main = function (paramsdate) {
var $targetformat = paramsdate.targetformat;
var $input = paramsdate.inputelement;
// 根据输入框的值初始化控件的值
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') {
// 移除其他日期选中状态
document.queryselectorall('.ui-datepicker-wrapper td').foreach(function (td) {
if (td.classlist.contains('active')) {
td.classlist.remove('active');
}
});
// 通过classname 设置选中日期
$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);
};
/* 定义一个函数,用于添加事件监听器,现代浏览器还是旧版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设置选中项
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); }
};
/* 日期时间格式化·结束 */
})();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;
}
/* 最外层区域 */
.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);
}
/* 头部区域 */
.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;
}
/* 设置两个按钮 */
.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区域 */
.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>
调用示例:
<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实现一个日期选择器(datepicker)组件的详细内容,更多关于js实现日期选择器的资料请关注代码网其它相关文章!
发表评论