对于时间选择器的插件,一开始是一头雾水,但是看了慕课网的学习视频和其他的参考资料,逐渐有了自己的想法,下面就简单的结合视频记录一下。参考视频连接:https://www.imooc.com/video/14520
首先要先写出来一个具体的时间弹框显示的样式
参考代码
<div>
<input type="text" class="g-input-style">
<div class="g-calendar-bg">
<div class="g-calendar-head">
<a href="" class="g-calendar-preYear"></a>
<a href="" class="g-calendar-preMonth"></a>
<a href="" class="g-calendar-nextYear"></a>
<a href="" class="g-calendar-nextMonth"></a>
<span>2019-10-17</span>
</div>
<div class="g-calender-body">
<table>
<tr>
<th>日</th>
<th>一</th>
<th>二</th>
<th>三</th>
<th>四</th>
<th>五</th>
<th>六</th>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
</tr>
<tr>
<td>15</td>
<td>16</td>
<td>17</td>
<td>18</td>
<td>19</td>
<td>20</td>
<td>21</td>
</tr>
<tr>
<td>22</td>
<td>23</td>
<td>24</td>
<td>25</td>
<td>26</td>
<td>27</td>
<td>28</td>
</tr>
<tr>
<td>29</td>
<td>30</td>
<td>31</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
</table>
</div>
<div class="g-calendar-operationBg">
<button class="g-calendar-operationBtn">清除</button>
<button class="g-calendar-operationBtn">确定</button>
</div>
</div>
</div>
image.png
就会出现一个类似图片中的样式框,就是要展示的日期插件的基本样式
下面就来思考,目前显示的具体日期和年月都是在页面上写好的,但是如何获取日期时间和如何把获取到的时间画到页面上就是接下来要思考的问题
先解决问题一,如何获取时间
声明一个datepicker,定义了一个方法,传2个参数,年份和月份;如果没有传参,默认获取当前的年份和月份;最后得到的dataArray输一个对象数组,每一个对象包含具体的日期,年份和月份,以及具体的星期,isOut用来判断是否不属于当前月份的日期
var datepicker = {}
datepicker.getMonthData = function (year, month) {
// 用来保存月份日期
var dayArray = []
// 获取年月日
if (!year || !month) {
var today = new Date()
year = today.getFullYear()
month = today.getMonth() + 1
}
console.log(year, month, 111111)
// 判断第一天是星期几
var firstDay = new Date(year, month-1, 1)
var currentWeekDay = firstDay.getDay()
// 判断月份最后一天是星期几,由此根据,需要前一个月份和后一个月份要补的天数是多少
var finalDay = new Date(year, month, 0)
var finalWeekDay = finalDay.getDay()
console.log(currentWeekDay, finalWeekDay, 22222)
// 获取月份有多少天,前一个月份有多少天,后一个月份有多少天
var totalDay = new Date(year, month, 0).getDate()
var beforeTotalDay = new Date(year, month-1, 0).getDate()
var nextTotalDay = new Date(year, month+1, 0).getDate()
console.log(new Date(year, month-1, 0),beforeTotalDay, totalDay, nextTotalDay, 3333)
// 极端情况有6周,最少有4周,按照6周计算
for (var i = 0; i < 7*6; i++) {
var preDay = +beforeTotalDay + i - currentWeekDay + 1
var showDay
var thisMonth
var thisYear
var weekDay
var isOut = false
thisYear = year
if (preDay <= beforeTotalDay) {
// 获取显示前一个月份的天
showDay = preDay
isOut = true
thisMonth = month - 1
if (thisMonth === 0) {
thisMonth = 12
thisYear = year - 1
}
} else {
// 获取月份正常显示的天
showDay = i - currentWeekDay + 1
thisMonth = month
if (showDay > totalDay) {
// 如果显示天数大于该月份天数,表示是下一个月
// 获取显示下一个月份的天
showDay = showDay - totalDay
thisMonth = month + 1
isOut = true
if (thisMonth === 13) {
thisMonth = 1
thisYear = year + 1
}
}
}
weekDay = new Date(thisYear, thisMonth - 1, showDay).getDay()
dayArray.push({
year: thisYear,
month: thisMonth,
day: showDay,
weekDay: weekDay,
isOut: isOut
})
}
console.log(dayArray, 44444)
return {
year: year,
month: month,
dayArray: dayArray
}
}
然后就是第二步-布局;这个时候就需要刚才写到html的内容放到js文件中
datepicker.buildUi = function (year, month) {
monthData = datepicker.getMonthData(year, month)
console.log(monthData, dataArray, 201901021)
var html = '<div class="g-calendar-head">' +
'<a href="#" class="g-calendar-preYear" />' +
'<a href="#" class="g-calendar-preMonth" />' +
'<a href="#" class="g-calendar-nextMonth" />' +
'<a href="#" class="g-calendar-nextYear"></a>' +
'<span>' + monthData.year + '-' + monthData.month + '</span>' +
'</div>' +
'<div class="g-calender-body">' +
'<table>' +
'<tr>' +
'<th>日</th>' +
'<th>一</th>' +
'<th>二</th>' +
'<th>三</th>' +
'<th>四</th>' +
'<th>五</th>' +
'<th>六</th>' +
'</tr>'
console.log(html, 123)
for (var i = 0; i < monthData.dayArray.length; i++) {
if (i % 7 === 0) {
html += '<tr>'
}
console.log(dataArray[1], monthData.month, 'monthData.month')
if (monthData.dayArray[i].isOut) {
// isOut为true,表示超过了当前月份的天数,则添加置灰样式
if (dataArray.length > 0 && +monthData.dayArray[i].day === +dataArray[2] && +monthData.dayArray[i].month === +dataArray[1] ) {
// 判断输入框原来选择的天数和月份跟循环的td的天数是 同一天,则添加选中样式
html += '<td class="g-calendar-td-gray g-calendar-td-common g-calender-td-select" data-realMonth="' + monthData.dayArray[i].month + '">' + monthData.dayArray[i].day + '</td>'
} else {
html += '<td class="g-calendar-td-gray g-calendar-td-common" data-realMonth="' + monthData.dayArray[i].month + '">' + monthData.dayArray[i].day + '</td>'
}
} else {
if (dataArray.length> 0 && +monthData.dayArray[i].day === +dataArray[2] && +monthData.dayArray[i].month === +dataArray[1]) {
// 判断输入框原来选择的天数和月份跟循环的td的天数是 同一天,则添加选中样式
html += '<td class="g-calendar-td-common g-calender-td-select" data-realMonth="' + monthData.dayArray[i].month + '">' + monthData.dayArray[i].day + '</td>'
} else {
html += '<td class="g-calendar-td-common" data-realMonth="' + monthData.dayArray[i].month + '">' + monthData.dayArray[i].day + '</td>'
}
}
var tdList = document.querySelectorAll('.g-calendar-td-common')
var lastTd = tdList[i]
console.log(lastTd, 'lastTd')
if (i % 7 === 6) {
html += '</tr>'
}
}
html += '</table>' +
'<div class="g-calendar-operationBg">' +
'<button class="g-calendar-operationBtn g-calendar-operationBtn-confirm">确定</button>' +
'<button class="g-calendar-operationBtn g-calendar-operationBtn-clear">清除</button>' +
'</div>' +
'</div>'
return html
}
然后需要思考的是,页面上箭头按钮事件触发,需要调用布局方法,进行新的布局
datepicker.create = function(direction) {
var year, month
if (monthData) {
year = monthData.year
month = monthData.month
}
if (direction === 'g-calendar-preMonth') month--
if (direction === 'g-calendar-nextMonth') month++
if (direction === 'g-calendar-preYear') year--
if (direction === 'g-calendar-nextYear') year++
bgDom = document.querySelector('.g-calendar-bg')
if (!bgDom) {
bgDom = document.createElement('div')
bgDom.className = 'g-calendar-bg'
document.body.appendChild(bgDom)
}
bgDom.innerHTML = datepicker.buildUi(year, month)
// 给不符合范围的日期样式
for (var i = 0; i < monthData.dayArray.length; i++) {
var tdList = document.querySelectorAll('.g-calendar-td-common')
var lastTd = tdList[i]
if (lastTd) {
var tdString = monthData.dayArray[i].year + '' + (monthData.dayArray[i].month < 10 ? '0' + monthData.dayArray[i].month : monthData.dayArray[i].month) + '' + (monthData.dayArray[i].day < 10 ? '0' + monthData.dayArray[i].day : monthData.dayArray[i].day)
var minString = minDataArray[0] + '' + (minDataArray[1] < 10 ? '0' + minDataArray[1] : minDataArray[1]) + '' + (minDataArray[2] < 10 ? '0' + minDataArray[2] : minDataArray[2])
var maxString = maxDataArray[0] + '' + (maxDataArray[1] < 10 ? '0' + maxDataArray[1] : maxDataArray[1]) + '' + (maxDataArray[2] < 10 ? '0' + maxDataArray[2] : maxDataArray[2])
if (+tdString < +minString || +tdString > +maxString) {
lastTd.classList.add('g-calendar-table-disable')
}
}
}
}
最后是初始化方法,但是初始化方法的时候不能显示出来日历组件,是要在点击输入框时才会触发日历的显示与隐藏事件;以及点击日历上的日期触发的方法和最后点击确定按钮时触发方法
var pageDom = document.querySelector(element) // 获取输入框
minData = min
maxData = max
minDataArray = min ? min.split('-') : []
maxDataArray = max ? max.split('-') : []
datepicker.create()
bgDom.classList.add('g-calendar-none')
var isClick = false
// 点击输入框,来判断显示还是隐藏日历组件
pageDom.addEventListener('click', function(e) {
dataArray = pageDom.value.split('-') // 获取输入框中显示的信息
if (dataArray.length > 0 && (dataArray[0] !== monthData.year || dataArray[1] !== monthData.month)) {
monthData.year = dataArray[0]
monthData.month = dataArray[1]
datepicker.create(dataArray[0], dataArray[1])
}
if (isClick) {
bgDom.classList.add('g-calendar-none')
isClick = false
} else {
bgDom.classList.remove('g-calendar-none')
isClick = true
var height = pageDom.offsetHeight
var top = pageDom.offsetTop
var left = pageDom.offsetLeft
bgDom.style.top = top + height + 2 + 'px'
bgDom.style.left = left + 'px'
}
e.stopPropagation()
})
var selectDay
bgDom.addEventListener('click', function (event) {
var e = event.target
console.log(e, 121212)
if (e.classList.contains('g-calendar-preMonth')) {
// 上一个月
datepicker.create('g-calendar-preMonth')
} else if (e.classList.contains('g-calendar-nextMonth')) {
// 下一个月
datepicker.create('g-calendar-nextMonth')
} else if (e.classList.contains('g-calendar-preYear')) {
// 上一年
datepicker.create('g-calendar-preYear')
} else if (e.classList.contains('g-calendar-nextYear')) {
// 下一年
datepicker.create('g-calendar-nextYear')
} else if (e.tagName.toLowerCase() === 'td') {
// 选中日期,添加选中样式
var selectTd = document.querySelector('.g-calender-td-select')
console.log(selectTd, 234)
selectDay = e.innerHTML
if (!selectTd && !e.classList.contains('g-calendar-table-disable')) {
console.log(e, 'selecttd')
e.classList.add('g-calender-td-select')
} else {
removeSelectClass()
if (!e.classList.contains('g-calendar-table-disable')) {
e.classList.add('g-calender-td-select')
}
}
} else if (e.tagName.toLowerCase() === 'button' && e.classList.contains('g-calendar-operationBtn-confirm')) {
// 确定
var realMonth = document.querySelector('.g-calender-td-select')
pageDom.value = monthData.year + '-' + realMonth.dataset.realmonth + '-' + selectDay
isClick = false
bgDom.classList.add('g-calendar-none')
} else if (e.tagName.toLowerCase() === 'button' && e.classList.contains('g-calendar-operationBtn-clear')) {
// 清除
pageDom.value = ''
isClick = false
bgDom.classList.add('g-calendar-none')
}
event.stopPropagation()
})
// 点击空白地方,日历弹框消失
document.body.addEventListener('click', function (e) {
bgDom.classList.add('g-calendar-none')
// bgDom.classList.add('g-calendar-none-animation')
isClick = false
})
// 移除选中的所有样式方法
function removeSelectClass() {
var list = document.getElementsByClassName('g-calendar-td-common')
console.log(list)
for (var i = 0; i < list.length; i++) {
list[i].classList.remove('g-calender-td-select')
}
}
至此,一个基础的日历组件基本就完成了
具体代码可以参考https://github.com/06guo/simpleCalendarDemo
第一次动手写,很多问题需要修改,但是基本思想应该不会有太大的出入。