用javascript写一个日历

1|0用javascript写一个日历

<!-- calendar container --> <div class="calendar-container"> <div class="shrink-btn">-</div> <h2>Calendar</h2> <!-- calendar --> <div class="calendar" id="calendar"> </div> <!-- calendar end --> <div class="calendar-btns"> <div class="calendar-btn btn">prev</div> <a href="../calendar/calendar.html"><div class="calendar-btn btn">show all</div></a> <div class="calendar-btn btn">next</div> </div> <div class="calendar-hidden-btn"></div> </div> <!-- calendar container end -->

1|11. 选择器

selector class desc
calendarContainer .calendar-container 日历的显示块
calendarContainerH2 .calendar-container h2 日历的年份文本显示
calendar .calendar 需要js插入日期的部分
calendarHiddenBtn .calendar-hidden-btn 将日历显示的动画按钮
shrinkBtn .shrink-btn 将日历隐藏的动画按钮
calendarBtn all(.calendar-btn) 获取了三个按钮包括向上一个月,向下一个月和显示全部月份

1|22. 定义变量

获取的当前的日期,用以标注日历中的当前日期,比如今天是2020年8月17号,则日历中的这一天可以相应标记。

变量名 描述
currentYear 获取当前的年份
currentCalendar 获取当前的日期
currentMonth 获取当前的月份
currentDate 获取当前的日
year 要渲染的年份
weekDays 存储所有的星期字符串
months 存储所有的月份字符串
const currentCalendar = new Date(); const currentMonth = currentCalendar.getMonth(); const currentDate = currentCalendar.getDate(); const currentYear = currentCalendar.getFullYear(); let year = currentYear; const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'Octomber', 'November', 'December'];

1|3

日历以一年为单位来渲染,所以就需要一个函数参数来传递需要渲染的年份,function insertDate(year){},函数内容如下:

calendarContainerH2.innerHTML = `${year} Calendar`; let dates = getAllDays(year); let monthsHTML = '';

日历中的年份文本标题需要通过传入的年份参数动态更改,用dates绑定一个存储了一年中所有日期的数组,这个数组通过getAllDays函数获取,它也接收一个year年份参数,因为年份不同日历会不一样。monthsHTML存储一个html字符串用来插入每一个月份的html模板。

function getAllDays(year) { // 获取这一年的第一天 const firstDay = new Date(`January 1 ${year}`); // console.log(firstDay); // 获取该年的最后一天 const lastDay = new Date(`December 31 ${year}`); // console.log(lastDay); // 获取这一年的所有日期,存入数组 const days = [firstDay]; // 追踪days数组是否已经存储完毕 let lastDayInArray = firstDay; while (lastDayInArray.getTime() !==lastDay.getTime()) { // addDays (需要增加的日期,增加的天数) days.push(addDays(lastDayInArray, 1)); lastDayInArray = days[days.length - 1]; } // console.log(days); return days; }

通过getAllDays函数获取一年中所有日期的数组,思路是获取这一年的第一天和最后一天,通过判断是否已经依次存入日期到最后一天来将所有日期存入数组,为了判断是否存入了最后一天,需要一个标记当前数组的最后一个元素,每次存入数组以后都将其与数组的最后一个元素绑定,在用它来与最后一天来判断。其中有一个addDays函数,它用来将天数加一天,以达到将每一天存入。

function addDays(date, days) { let result = new Date(date); result.setDate(result.getDate() + days); return result; }

Date.getDate()方法返回的是日,是number对应 1 - 31

months.forEach((months, index) => { monthsHTML += ` <div class="months months_${index}"> <h3>${months}</h3> <div class="week_days_container"> ${weekDays.map((day) => `<div class="week_days">${day}</div>`).join('')} </div> <div class="days_container"> </div> </div>`; }); calendar.innerHTML = monthsHTML;

months数组中存储了一年的十二个月,通过foreach将数组中的每一个月遍历,叠加monthsHTML,会将12个月的html模板叠加入monthsHTML,其中每个月的星期都是7个,也需要循环加入,最后设置calendar的innerHTML。模板里的months_${index}可以更好地追踪月份对应地dom,days_container用来放置日节点。

dates.forEach((date) => { // 获取月份 const month = date.getMonth(); // console.log(month); // 获取月份对应的日期节点 const monthEl = document.querySelector(`.months_${month} .days_container`); // 在每个月的第一天前面创建空白 // getDate() 返回月份的某一天 (1-31) // getDay() 返回的是日期对应的星期 (0-6) if (date.getDate() === 1 && date.getDay() !== 0) { for (let i = 0; i < date.getDay(); i++) { const emptySpot = createEmptySpot(); monthEl.appendChild(emptySpot); } } const dateEl = createDateEl(date); monthEl.appendChild(dateEl); });

插入日的思路是,date中有月、星期、日,在同一个一个date元素中getMonth获取了月份,getDate就获取了该月的日期,从浏览器控制台看date就能清楚

,而因为每一个月的1号对应的星期都不一样,所以必须插入相应的空白节点,dates中存储了所有日期对象,为每一个日期对象获取月份,并选择对应的dom节点,这样就可以在对应的节点中插入对应的日。Date.getDay()方法返回的是星期,也是number,不过是以0开始,即 0-6,通过判断当日为一号,而且不为星期一时,因为如果一号就是星期一那么就不需要插入空白节点了,空白节点的数量是刚好与一号对应的星期数相同,比如一号对应的是星期三,即getDay是2,而日历中也是星期一和星期二两处空白。createEmptySpot就是用来创建空白节点的函数,然后把空白节点插入对应月份的节点。插入完空白的节点,就需要插入日节点了,createDateEl为创建日节点的函数,然后插入到对应的月份节点。

function createEmptySpot() { const emptyEl = document.createElement('div'); emptyEl.classList.add('days'); return emptyEl; }

上面函数创建的html模板是:

<div class="days"></div>
function createDateEl(date) { const day = date.getDate(); const dateEl = document.createElement('div'); dateEl.classList.add('days'); dateEl.innerHTML = `<span class="circle date_${day}">${day}</span>`; return dateEl; }

这个函数用day接收日期的日,创建的html模板为:

<div class="days"> <span class="circle date_..">..</span> </div>

以上就已经完成了渲染一整年日历的工作,但是一般都是以一个月显示并且可以来回切换月份和年份,我以function calendarAnimation(){}函数将这部分工作封装起来:

// 显示的月份 let displayMonth = currentMonth; let displayMonthEl = document.querySelector(`.months_${displayMonth}`); displayMonthEl.style.display = 'block'; // 日历显示动画 const monthsContainer = document.querySelectorAll('.months'); console.log(monthsContainer);

这部分为获取选择器,displayMonth用来标记需要显示到面板的月份,没有被标记的月份需要隐藏起来,需要显示的节点用displayMonthEl来动态获取。而monthsContainer是用来获取日历显示面板的,用来更好的实现面板的移动或隐藏。

calenarHiddenBtn.addEventListener('click', containerBack); shrinkBtn.addEventListener('click', containerMove); calendarBtn[0].addEventListener('click', monthChange); calendarBtn[2].addEventListener('click', monthChange);

创建按钮点击事件,这里涉及到3个函数,containerBack函数用来显示日历面板,containerMove函数用来隐藏日历面板,monthChange用来切换显示的月份。

function monthChange(event) { if (event.target === calendarBtn[0]) { if (displayMonth >= 0 && displayMonth <= 11) { displayMonthEl.style.display = 'none'; displayMonth--; if (displayMonth < 0) { year--; insertDate(year); displayMonth = 11; } displayMonthEl = document.querySelector(`.months_${displayMonth}`); displayMonthEl.style.display = 'block'; } } else { if (displayMonth >= 0 && displayMonth <= 11) { displayMonthEl.style.display = 'none'; displayMonth++; if (displayMonth > 11) { year++; insertDate(year); displayMonth = 0; } displayMonthEl = document.querySelector(`.months_${displayMonth}`); displayMonthEl.style.display = 'block'; } } }

在monthChange函数中,传入event,用event.target来更好判断是哪个按钮在点击,向前一个月,则需要先让当前月份隐藏,让displayMonth-- 变成前一个月并选择前一个月的节点显示,如果displayMonth<0了就说明需要往前一年,这时就需要year - 1并重新渲染日历了,需要重新调用insertDate(year),还要将应显示的月份设置为最后一个月。往后一个月的操作是同样的思路。

function containerMove() { calendarContainer.style.left = '-332px'; calenarHiddenBtn.style.display = 'block'; } function containerBack(){ calendarContainer.style.left = '20px'; calenarHiddenBtn.style.display = 'none'; }

这两个函数是一个简单的移动动画

if (year === currentYear) { const currentMonthEl = document.querySelector(`.months_${currentMonth}`); const currentDateEl = currentMonthEl.querySelector(`.date_${currentDate}`); currentDateEl.classList.add('active'); }

最后,获取你今天的日期,用以标注当日日期。

// 启动 insertDate(year); calendarAnimation();

1|4总结

在这个项目中,让我对时间的操作有了很多的实践,getFullYear() getMonth() getDate() getDay()。数组的forEach map,后者会返回一个新的数组,可以用join来将其组合成字符串,在${}中如果内容是一个值会将其值输出。

最后,javascript是面向对象的,我却写得像面向过程,主要还是没有理解对象,不知道该如何选取对象,需要好好去理解一下,或许写一个小游戏的项目会帮助我的理解。


__EOF__

本文作者··十方··
本文链接https://www.cnblogs.com/flynn24/p/13518056.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ··十方··  阅读(679)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示