在日常开发中,大多数都是在和框架打交道,久而久之便遗忘了原生JS的感觉,个人感觉中原生JS基础还是很重要的,所以最近就利用了空余时间造一个轮子出来,虽然以我的水平造出来的轮子质量还是不太可靠的,但是我觉得用来练练手还是不错的,哈哈!!
以下是日历的样子,是有点难看,讲究讲究,重点在于JS部分,嘻嘻!!!
关于日历组件的实现思路
- 设置默认参数
- 检查节点参数是否传入,否则抛出错误
- 动态创建显示本日星期几的横轴
- 动态创建日历的日子
- 最后添加一点dom动作就好
先来看看构造函数内容
constructor(options) {
let defaluteOptions = {
element: null, //这是节点
startOfWeek: 1,
strings: {
week: n => {
let map = {
0: '周日',
1: '周一',
2: '周二',
3: '周三',
4: '周四',
5: '周五',
6: '周六',
}
return map[n];
},
templateDay: `<li class="currentMonth">
<span class="dayLabel">
<span class="day"></span>
<span class="unit">日</span>
</span>
</li>`
},
days: {},
}
//赋值默认参数
this.options = Object.assign({}, defaluteOptions, options);
//轮番就调用函数动态创建dom
this.checkOptions()._generateTime()._generateWeekDay()._generatePrevMonth();
复制代码
初始化创建Calendar类对象的时候设置数值,赋值默认参数以及调用方法来动态创建dom,相信小伙伴们看懂这段代码没压力。
该轮子我全程都是用ES6写的,毕竟程序员还是要跟上潮流的!!
赋值参数后开始轮番调用函数,首先调用的是this.checkOptions()方法,检查节点是否存在
checkOptions() {
//如果节点不存在直接抛出错误
if (!this.options.element) {
throw new Error('element is request');
}
return this;
}
复制代码
接下来就是获取当天的年月日
毕竟是日历,获取当前的年月日当参考还是很重要的
_generateTime() {
let data = new Date(); //时间
let year = this.options.days.year = data.getFullYear(); //年份
let month = this.options.days.month = data.getMonth() + 1; //月份
let day = this.options.days.day = data.getDate(); //日子
this.options.days.countDay = 0; //日历总日子数为7*6=42
this.options.days.noMonth = data.getMonth() + 1; //不变的月份
this.options.days.noYear = data.getFullYear(); //不变的年份
return this;
}
复制代码
创建星期横轴
_generateWeekDay() {
let {
startOfWeek,
strings
} = this.options;
let calendar = document.querySelector('.calendar');
let ol = dom.create(`<ol class="weekdays"></ol>`);
calendar.appendChild(ol);
let weekIndex = this.createArray(7, startOfWeek).map((day, i) => {
let li = dom.create(`<li>${strings.week(i)}</li>`);
//判断是否为今天
ol.appendChild(li);
});
return this;
}
复制代码
dom.create是封装好的方法,传入模板即可创建并返回回来
this.createArray()也是封装好的方法,本函数是创建一个长度为7的数组,为什么长度为7?因为周一到周日的长度为7啊,然后开始使用map映射和遍历来创建节点并添加document.body里面!!!
唔唔唔,去到这里,星期横轴就创建好了,接下来是重点部分了,就是创建对于的星期的日子日历,其实只要掌握逻辑就好了,不过因为我是菜鸡,写的时候也有点掉坑,所以,哈哈,你们对我写的代码参考参考就好了!!
接下来是重点了,就是创建日子
创建日历日子分为三个部分,第一部分是上个月的日子,第二是本月的日子,第三部分是下个月的日子,三个部分所以把它们分别封装起来,嫑相互影响!!
话不多说,贴上代码
//创建上个月日子
_generatePrevMonth() {
let date = this.options.days;
let year = date.year;
let month = date.month;
let beginWeek = this._getWeekWeek(year, month - 1, 1); //开始星期
let countMonth = this._getMonth(year, month - 1); //上月月份天数
let calendar = document.querySelector('.calendar');
let ol = dom.create(`<ol class="days"></ol>`);
let li = document.querySelectorAll('.dayLabel>.day');
calendar.appendChild(ol);
date.countDay = 0;
beginWeek == 0 ? beginWeek += 7 : ''; //如果月份开头为星期日,会出bug,这是防止
date.countDay += beginWeek;
this.createArray(42, this.options.startOfWeek).map((day, i) => {
let li = dom.create(this.options.strings.templateDay);
let span = li.querySelector('.dayLabel>.day');
if (i < beginWeek) {
span.textContent = countMonth - beginWeek + 1 + i;
}
ol.appendChild(li);
});
document.querySelector('h1.date').appendChild(dom.create(`<p data-role="time">${date.year}-${date.month}-${date.day}</p>`));
this._generateCurrentDay()._generateNextMonth();
}
复制代码
创建日子从头到尾,首先是创建上月的日子,逻辑就是先创建一个长度为42的数组,因为6*7=42,数组下标为0至41,然后获取上月天数以及本月一号开始是星期几,这样就能知道上月日子占位,比如5月1号是星期三,那么上个月的日子就占了3位置,然后通过循环赋值,这样就创建了日历上月的天数!!
有种特殊情况就是1号是星期天,beginWeek为0,而上月天数占比需要为7,所以就需要+7,要不然会出现问题
然后是创建本月的天数
按照惯例,贴上代码
//创建当前月份日子
_generateCurrentDay() {
let date = this.options.days;
let getWeek = this._getWeekWeek(date.year, date.month - 1, date.day); //星期几
let getMonth = this._getMonth(date.year, date.month) //月份天数
let getMonthDay = this._getWeekDay(); //几号
let li = document.querySelectorAll('.dayLabel>.day');
//创建当月日子模块
let dayIndex = this.createArray(getMonth, this.options.startOfWeek).map((day, i) => {
//判断日历起止
i += date.countDay;
li[i].textContent = i - date.countDay + 1;
//判断是否为今天
if (i == (getMonthDay + date.countDay - 1) && date.noMonth == date.month && date.noYear == date.year) {
li[i].parentNode.classList.add('today');
}
});
date.countDay += getMonth;
return this;
}
复制代码
创建当前月份日子,首先是知道本月月份的天数getMonth,然后创建长度为getMonth的数组,本月开始的下标为(0+data.countDay),然后通过循环赋值,顺便判断是否为今天,如果是今天就添加today的class,唔唔唔,大概就是如此~~~
最后就是下一个月的天数
代码 代码 代码
//创建下个月日子
_generateNextMonth() {
let date = this.options.days;
let year = date.year;
let month = date.month;
let beginWeek = this._getWeekWeek(year,month,1);//开始星期
let countMonth = this._getMonth(year,month+1);//下月月份天数
let li = document.querySelectorAll('.dayLabel>.day');
//data.countDay统计了上月和本月的日子数总量,直接减去即可
this.createArray(42-date.countDay , this.options.startOfWeek).map((day,i)=>{
li[date.countDay+i].textContent = i+1;
});
}
复制代码
这个逻辑比较简单,就是用(6*7=42)42减去上月天数和本月天数,剩下的位置为显示下个月的天数,所以就是这样子!!!
把封装好的代码也弄出来吧~~
//dom.create()调用
let dom = {
create(html) {
let template = document.createElement('template');
template.innerHTML = html;
return template.content.firstChild;
}
}
复制代码
//this.createArray()调用
//创建数组节点
createArray(length, fill) {
let array = Array.apply(null, {
length: length
}).map(() => fill);
return array;
}
//获取月份天数
_getMonth(year, month) {
return new Date(year, month, 0).getDate();
}
//获取星期几
_getWeekWeek(year, month, day) {
return new Date(year, month, day).getDay();
}
//获取当前月份日子
_getWeekDay() {
return new Date().getDate();
}
复制代码
动作切换部分
切换日子这里相对来说就是比较简单,我直接贴代码,你们一看就懂了
//上一个月
previousMonth() {
// this.options.days.month -= 1;
this.changeMonth('prev');
}
//下一个月
nextMonth() {
// this.options.days.month += 1;
this.changeMonth('next');
}
//回到今天
resetMonth() {
// this._generateTime();
this.changeMonth('defalut');
}
//封装月份dom
changeMonth(status) {
let date = this.options.days;
switch(status) {
case 'prev': {
--date.month < 1 ? date.year-- ? date.month = 12 : '' : '';
break;
}
case 'next': {
++date.month > 12 ? date.year++ ? date.month = 1 : '' : '';
break;
}
case 'defalut': {
this._generateTime();
break;
}
}
//移除节点
this._generateCalendar();
//重新添加节点
this._generatePrevMonth();
}
复制代码
调用方式
let calendar = new Calendar({
element: document.querySelector('.calendar')
})
//切换上月
previousMonth.onclick = function () {
calendar.previousMonth()
}
//切换下月
nextMonth.onclick = function () {
calendar.nextMonth()
}
//回到今月
today.onclick = function () {
calendar.resetMonth()
}
复制代码
整个日历组件都是通过this.options.days.month和this.options.days.year这两参数来生成相对于的日历数据,所以你想要切换月份只需修改这两个参数即可!!!
整个逻辑思路都是自己想的,所以可能并不算得上是个很好的想法,所以仅供参考!
唔唔唔,整个日历组件下来大概就是这样子,整个流程写下来感觉自己的思维还是有所进步的,但是其实我觉得这个轮子代码还是可以再封装封装和完善的,嘻嘻~~
轮子功能比较简单,所以剩下的功能就等待小伙伴们自由发挥了~~