js实现日历


Posted in Javascript onNovember 07, 2020

这周写自己的项目发现又用到日历了,加之自己毕业之后的第一个工作中遇到的任务也是需要写个日历(组员写了,我就不用写了)
今天就来好好折腾一下日历是怎么写的。

首先,我们看看 windows 的日历。发现总共有这么几个元素。先实现试试。

1.年份的选择、月份的选择
2.周一 ~ 周日(周日 ~ 周六)
3.日历格子 6*7 = 42

从数据的角度来分析日历的实现是比较简单的
1.我们需要显示一个当前时间的结构 - new Date()
2.我们需要显示当月的信息 - [星期(周一~周日),日期(1-[28,29,30,31])]
其中我们只要知道了每个月的 1日 是星期几,就能很容易地摆放后面的日子(万事开头难)。

  • 我们最多需要 6 行来显示我们的日期,因为要第一排如果只包含本月的一天 6(上个月的) + (1 + 4*7),这样就五行了,当月天数若大于 29,就显示不下了
  • 确定了 6 行之后,我们发现我们可能需要获取上个月,和下个月多出来的几天的摆放位置。
  • 不同年份的不同月的 2月份,我们知道它的日期是不同的,所以我们还需要判断 平年还是闰年。

3.显示上个月,下个月的切换。我们发现需要有个函数来帮我们更新日历。

分析完之后,让我们跟着 新增/修改 一些代码。

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
<style>
 .week-item {
  display: inline-block;
  width: 80px;
  height: 40px;
  line-height: 40px;
  border: 1px solid sandybrown;
  text-align: center;
 }
 .date-item {
  display: inline-block;
  width: 80px;
  height: 40px;
  line-height: 40px;
  border: 1px solid beige;
  text-align: center;
 }
</style>
</head>
<body>
 <div class="wrapper">
  <div class="year-line">
   <button id="preMonth" class="year-prev">上一月</button>
   <button id="nowYear" class="year-now"></button>
   <button id="nowMonth"></button>
   <button id="nowDate"></button>
   <button id="nextMonth" class="year-next">下一月</button>
  </div>
  <div id="weekLine" class="week-line"></div>
  <div id="dateWrap" class="date-wrap"></div>
 </div>
</body>
<script>
 // 工具方法 - start
 // 1.为了获得每个月的日期有多少,我们需要判断 平年闰年[四年一闰,百年不闰,四百年再闰]
 const isLeapYear = (year) => {
  return (year % 400 === 0) || (year % 100 !== 0 && year % 4 === 0);
 };
 // 2.获得每个月的日期有多少,注意 month - [0-11]
 const getMonthCount = (year, month) => {
  let arr = [
   31, null, 31, 30, 
   31, 30, 31, 31,
   30, 31, 30, 31
  ];
  let count = arr[month] || (isLeapYear(year) ? 29 : 28);
  return Array.from(new Array(count), (item, value) => value + 1);
 };
 // 3.获得某年某月的 1号 是星期几,这里要注意的是 JS 的 API-getDay() 是从 [日-六](0-6),返回 number
 const getWeekday = (year, month) => {
  let date = new Date(year, month, 1);
  return date.getDay();
 };
 // 4.获得上个月的天数
 const getPreMonthCount = (year, month) => {
  if (month === 0) {
   return getMonthCount(year - 1, 11);
  } else {
   return getMonthCount(year, month - 1);
  }
 };
 // 5.获得下个月的天数
 const getNextMonthCount = (year, month) => {
  if (month === 11) {
   return getMonthCount(year + 1, 0);
  } else {
   return getMonthCount(year, month + 1);
  }
 };
 // 工具方法 - end
 let weekStr = '日一二三四五六';
 weekArr = weekStr.split('').map(item => '星期' + item);
 // 插入星期 dom
 let weekDomStr = '';
 let oFragWeek = document.createDocumentFragment();
 weekArr.forEach(item => {
  let oSpan = document.createElement('span');
  let oText = document.createTextNode(item);
  oSpan.appendChild(oText);
  oSpan.classList.add('week-item');
  oFragWeek.appendChild(oSpan);
 });
 let weekWrap = document.getElementById('weekLine');
 weekWrap.appendChild(oFragWeek);

 // 这里获得我们第一次的 数据 数组
 const updateCalendar = (year, month, day) => {
  if (typeof year === 'undefined' && typeof month === 'undefined' && typeof day === 'undefined') {
   let nowDate = new Date();
   year = nowDate.getFullYear();
   month = nowDate.getMonth();
   day = nowDate.getDate();
  }
  // 更新一下顶部的年月显示
  document.getElementById('nowYear').innerHTML = year;
  document.getElementById('nowMonth').innerHTML = month + 1;
  document.getElementById('nowDate').innerHTML = day;
  // 生成日历数据,上个月剩下的的 x 天 + 当月的 28(平年的2月)或者29(闰年的2月)或者30或者31天 + 下个月的 y 天 = 42
  let res = [];
  let currentMonth = getMonthCount(year, month);
  let preMonth = getPreMonthCount(year, month);
  let nextMonth = getNextMonthCount(year, month);
  let whereMonday = getWeekday(year, month);
  if (whereMonday === 0) {
   whereMonday = 7
  }
  // 感谢网友 luoyiming 的测试(哈哈!谢谢!):这里当 whereMonday 为 0 的时候会截取上月的所有数据
  let preArr = preMonth.slice(-1 * whereMonday)
  let nextArr = nextMonth.slice(0, 42 - currentMonth.length - whereMonday);
  res = [].concat(preArr, currentMonth, nextArr);
  // 上面经过我本人的测试是没有什么问题,接下来就是更新 dom 的信息的问题
  let hadDom = document.getElementsByClassName('date-item');
  if (hadDom && hadDom.length) {
   let domArr = document.getElementsByClassName('date-item');
   for (let i = 0; i < domArr.length; i++) {
    domArr[i].innerHTML = res.shift();
   }
  } else {
   // 如果之前没有结构的话
   let str = '';
   for (let i = 0; i < 6; i++) {
    str += '<div class="date-line">';
    for (let j = 0; j < 7; j++) {
     str += `<span class='date-item'>${res.shift()}</span>`;
     if (j === 6) {
      str += '</div>';
     }
    }
   }
   document.getElementById('dateWrap').innerHTML = str;
  }
 };

 updateCalendar();
 // 添加上一月,下一月事件
 let oPreButton = document.getElementById('preMonth');
 let oNextButton = document.getElementById('nextMonth');
 oPreButton.addEventListener('click', function () {
  let currentYear = +document.getElementById('nowYear').textContent;
  let currentMonth = +document.getElementById('nowMonth').textContent - 1;
  let currentDate = +document.getElementById('nowDate').textContent;
  if (currentMonth === 0) {
   updateCalendar(currentYear - 1, 11, currentDate);
  } else {
   updateCalendar(currentYear, currentMonth - 1, currentDate);
  }
 });
 oNextButton.addEventListener('click', function () {
  let currentYear = +document.getElementById('nowYear').textContent;
  let currentMonth = +document.getElementById('nowMonth').textContent - 1;
  let currentDate = +document.getElementById('nowDate').textContent;
  if (currentMonth === 11) {
   updateCalendar(currentYear + 1, 0, currentDate);
  } else {
   updateCalendar(currentYear, currentMonth + 1, currentDate);
  }
 });
</script>
</html>

发现用 dom 直接操作而不是通过 mvvm 框架实现确实还是比较蛋疼的,以下是这次实现的效果。

实现一个功能的时候,从数据的层面分析,有时候会比较容易理解

以上就是js实现日历的详细内容,更多关于js 日历的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
JavaScript中几种常见排序算法小结
Feb 22 Javascript
javascript检查表单数据是否改变的方法
Jul 30 Javascript
弹出最简单的模式化遮罩层的js代码
Dec 04 Javascript
node.js使用nodemailer发送邮件实例
Mar 10 Javascript
学习JavaScript设计模式之观察者模式
Apr 22 Javascript
Angular 4依赖注入学习教程之组件服务注入(二)
Jun 04 Javascript
vue中使用gojs/jointjs的示例代码
Aug 24 Javascript
vue 属性拦截实现双向绑定的实例代码
Oct 24 Javascript
Vue实现一个图片懒加载插件
Mar 11 Javascript
微信小程序前端自定义分享的实现方法
Jun 13 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
Sep 10 Javascript
Nest.js环境变量配置与序列化详解
Feb 21 Javascript
工作中常用js功能汇总
Nov 07 #Javascript
解决VUE 在IE下出现ReferenceError: Promise未定义的问题
Nov 07 #Javascript
解决Element中el-date-picker组件不回填的情况
Nov 07 #Javascript
解决element-ui的下拉框有值却无法选中的情况
Nov 07 #Javascript
解决VUE项目使用Element-ui 下拉组件的验证失效问题
Nov 07 #Javascript
详解datagrid使用方法(重要)
Nov 06 #Javascript
VUE异步更新DOM - 用$nextTick解决DOM视图的问题
Nov 06 #Javascript
You might like
php实现文件下载(支持中文文名)
2013/12/04 PHP
yii 2.0中表单小部件的使用方法示例
2017/05/23 PHP
PHP编程求最大公约数与最小公倍数的方法示例
2017/05/29 PHP
js url传值中文乱码之解决之道
2009/11/20 Javascript
jquery多浏览器捕捉回车事件代码
2010/06/22 Javascript
jquery选择器的选择使用及性能介绍
2013/01/16 Javascript
js实现特定位取反原理及示例
2014/06/30 Javascript
javascript控制图片播放的实现代码
2020/07/29 Javascript
老生常谈jquery中detach()和remove()的区别
2017/03/02 Javascript
jQuery实现的手风琴侧边菜单效果
2017/03/29 jQuery
将 vue 生成的 js 上传到七牛的实例
2017/07/28 Javascript
VUE中的无限循环代码解析
2017/09/22 Javascript
vue小白入门教程
2018/04/02 Javascript
JS集合set类的实现与使用方法示例
2019/02/01 Javascript
javascript异步处理与Jquery deferred对象用法总结
2019/06/04 jQuery
vue 使用element-ui中的Notification自定义按钮并实现关闭功能及如何处理多个通知
2019/08/17 Javascript
wxpython中利用线程防止假死的实现方法
2014/08/11 Python
Python实现从url中提取域名的几种方法
2014/09/26 Python
5种Python单例模式的实现方式
2016/01/14 Python
Python多线程、异步+多进程爬虫实现代码
2016/02/17 Python
详解Python下ftp上传文件linux服务器
2018/06/21 Python
python中单下划线_的常见用法总结
2018/07/10 Python
利用pandas将非数值数据转换成数值的方式
2019/12/18 Python
pandas读取csv文件提示不存在的解决方法及原因分析
2020/04/21 Python
python 爬取B站原视频的实例代码
2020/09/09 Python
python 实现简单的计算器(gui界面)
2020/11/11 Python
Python中对象的比较操作==和is区别详析
2021/02/12 Python
HTML5+CSS3实现拖放(Drag and Drop)示例
2014/07/07 HTML / CSS
澳大利亚电商Catch新西兰站:Catch.co.nz
2020/05/30 全球购物
自考自我鉴定范文
2013/10/30 职场文书
公司年会晚宴演讲稿
2014/01/06 职场文书
公司庆典活动邀请函
2014/01/09 职场文书
小学校长先进事迹材料
2014/05/13 职场文书
流动人口婚育证明范本
2014/09/26 职场文书
实习护士自荐信
2015/03/25 职场文书
2015年医院科室工作总结范文
2015/05/26 职场文书