持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
本文通过用原生javascript写一个datepicker组件来锻炼自己原生js能力,组件效果如下图所示:
JavaScript Date 对象
生成日期对象
var d = new Date();
var d = new Date(milliseconds); // 参数为毫秒
var d = new Date(dateString);
var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);
Date 对象方法
| 方法 | 描述 |
|---|---|
| getDate() | 从 Date 对象返回一个月中的某一天 (1 ~ 31)。 |
| getDay() | 从 Date 对象返回一周中的某一天 (0 ~ 6)。 |
| getFullYear() | 从 Date 对象以四位数字返回年份。 |
| getHours() | 返回 Date 对象的小时 (0 ~ 23)。 |
| getMilliseconds() | 返回 Date 对象的毫秒(0 ~ 999)。 |
| getMinutes() | 返回 Date 对象的分钟 (0 ~ 59)。 |
| getMonth() | 从 Date 对象返回月份 (0 ~ 11)。 |
| getSeconds() | 返回 Date 对象的秒数 (0 ~ 59)。 |
| getTime() | 返回 1970 年 1 月 1 日至今的毫秒数。 |
【注意】
- 月份需要减1,比如生成6月1的日期,那么new Date(2022, 5, 1)
- 周日显示为0,不是7
- 越界自动进位
当月的最后一天
new Date(2022, 6, 0) // 得到6月最后一天,即6月30号
生成日期数据
- 首先利用自执行函数来包裹具体的内容,形成模块化,防止污染外部变量
- 当月第一天和当月最后一天
var firstDay = new Date(year, month - 1, 1)
var lastDay = new Date(year, month, 0)
;(function () {
var datepicker = {}
datepicker.getMonthData = 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)
// 年和月有越界的问题,所以在这里重新获取一次
year = firstDay.getFullYear()
month = firstDay.getMonth() + 1
// 当月的第一天是星期几
var firstDayWeekDay = firstDay.getDay()
if (firstDayWeekDay === 0) {
firstDayWeekDay = 7
}
// 上月的最后一天
var lastDayOfLastMonth = new Date(year, month - 1, 0)
var lastDateOfLastMonth = lastDayOfLastMonth.getDate()
// 需要补多少个上月的日期
var preMonthDayCount = firstDayWeekDay - 1
// 当月的最后一天
var lastDay = new Date(year, month, 0)
lastDate = lastDay.getDate()
// 有的月份只占据4行,比如28天,有的月份占据6行,比如1号在星期日的位置,然后有31号,那么就占据了6行,所以这里显示6 * 7
for (var i = 0; i < 7 * 6; i++) {
var date = i + 1 - preMonthDayCount
var showDate = date
var thisMonth = month
if (date <= 0) {
// 上一个月
thisMonth = month - 1
showDate = lastDateOfLastMonth + date
} else if (date > lastDate) {
// 下一个月
thisMonth = month + 1
showDate = showDate - lastDate
}
if (thisMonth === 0) {
// 上一年的第12月
thisMonth = 12
}
if (thisMonth === 13) {
// 下一年的第1个月
thisMonth = 1
}
ret.push({
month: thisMonth,
date: date,
showDate: showDate
})
}
return {
days: ret,
year: year,
month: month
}
}
window.datepicker = datepicker
})()
js 动态渲染
拼接内容的html字符串
datepicker.buildUi = function (year, month) {
monthData = datepicker.getMonthData(year, month)
var html =
'<div class="ui-datepicker-header">' +
'<a href="#" class="ui-datepicker-btn ui-datepicker-prev-btn"><</a>' +
'<a href="#" class="ui-datepicker-btn ui-datepicker-next-btn">></a>' +
'<span class="ui-datepicker-current-month">' +
monthData.year +
'-' +
monthData.month +
'</span>' +
'</div>' +
'<div class="ui-datepicker-body">' +
'<table>' +
'<thead>' +
'<tr>' +
'<th>一</th>' +
'<th>二</th>' +
'<th>三</th>' +
'<th>四</th>' +
'<th>五</th>' +
'<th>六</th>' +
'<th>日</th>' +
'</tr>' +
'</thead>' +
'<tbody>'
for (var i = 0; i < monthData.days.length; i++) {
var data = monthData.days[i]
if (i % 7 === 0) {
// 能被7整除,说明是这一周的第一天
html += '<tr>'
}
html += '<td data-date="' + data.date + '">' + data.showDate + '</td>'
if (i % 7 === 6) {
html += '</tr>'
}
}
html += '</tbody>' + '</table>' + '</div>'
return html
}
渲染html字符串
datepicker.render = function () {
var html = datepicker.buildUi(year, month)
div = document.querySelector('.ui-datepicker-wrapper')
if (!div) {
div = document.createElement('div')
document.body.appendChild(div)
div.className = 'ui-datepicker-wrapper'
}
div.innerHTML = html
}
datepicker组件
input框
<style>
.datepicker {
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px;
height: 24px;
line-height: 24px;
width: 230px;
}
.datepicker:focus {
outline: none;
border: 1px solid #1abc9c;
}
</style>
<input type="text" class="datepicker" />
当点击input框的时候弹出日期组件,再次点击关闭日期组件,这里使用classList来进行显示与隐藏。
日历组件需要跟随input框的位置,所以首先把日期组件进行决定定位postion: absolute,然后在js中根据input框的位置实时的改变top left的值。
实时计算input框的位置:
var left = $input.offsetLeft
var top = $input.offsetTop
var height = $input.offsetHeight
var $input = document.querySelector(input)
var isOpen = false
$input.addEventListener('click', function () {
if (isOpen) {
div.classList.remove('ui-datepicker-wrapper-show')
isOpen = false
} else {
div.classList.add('ui-datepicker-wrapper-show')
var left = $input.offsetLeft
var top = $input.offsetTop
var height = $input.offsetHeight
div.style.left = left + 'px'
div.style.top = top + height + 2 + 'px'
isOpen = true
}
},
false
)
点击日期进行选择
new Date(year, month, day)第三个参数可以为负数,如果是负数那么就是往上个月倒退,比如new Date(2022,5, -2),得到的日期是5月29号,也就是倒退了2天。
div.addEventListener('click', function (e) {
var $target = e.target
if ($target.tagName.toLowerCase() !== 'td') {
return
}
var date = new Date(
monthData.year,
monthData.month - 1,
$target.dataset.date
)
$input.value = format(date)
div.classList.remove('ui-datepicker-wrapper-show')
isOpen = false
},
false
)