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开发技术大全-第1章javascript概述
Jul 03 Javascript
js自定义事件及事件交互原理概述(二)
Feb 01 Javascript
使用jquery解析XML的方法
Sep 05 Javascript
jQuery中dom元素上绑定的事件详解
Apr 24 Javascript
纯JS实现只能输入数字的简单代码
Jun 21 Javascript
使用 vue.js 构建大型单页应用
Feb 10 Javascript
vueScroll实现移动端下拉刷新、上拉加载
Mar 22 Javascript
Vue图片浏览组件v-viewer用法分析【支持旋转、缩放、翻转等操作】
Nov 04 Javascript
在Vue中实现随hash改变响应菜单高亮
Mar 09 Javascript
微信小程序订阅消息(java后端实现)开发
Jun 01 Javascript
Ant Design Vue table中列超长显示...并加提示语的实例
Oct 31 Javascript
JavaScript offset实现鼠标坐标获取和窗口内模块拖动
May 30 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
WordPress中访客登陆实现邮件提醒的PHP脚本实例分享
2015/12/14 PHP
详解PHP匿名函数与注意事项
2016/03/29 PHP
浅谈PHP安全防护之Web攻击
2017/01/03 PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
2018/02/19 PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
2019/09/16 PHP
express的中间件cookieParser详解
2014/12/04 Javascript
详解AngularJS中的作用域
2015/06/17 Javascript
如何通过js实现图片预览功能【附实例代码】
2016/03/30 Javascript
Avalon中文长字符截取、关键字符隐藏、自定义过滤器
2016/05/18 Javascript
js判断输入字符串是否为空、空格、null的方法总结
2016/06/14 Javascript
Jquery实现跨域异步上传文件总结
2017/02/03 Javascript
vue中element组件样式修改无效的解决方法
2018/02/03 Javascript
详解如何用babel转换es6的class语法
2018/04/03 Javascript
Nodejs把接收图片base64格式保存为文件存储到服务器上
2018/09/26 NodeJs
详解Angular中实现自定义组件的双向绑定的两种方法
2018/11/23 Javascript
Three.js实现简单3D房间布局
2018/12/30 Javascript
JS使用iView的Dropdown实现一个右键菜单
2019/05/06 Javascript
JavaScript Html实现移动端红包雨功能页面
2021/01/10 Javascript
[03:16]DOTA2完美大师赛小组赛精彩集锦
2017/11/22 DOTA
Python的Asyncore异步Socket模块及实现端口转发的例子
2016/06/14 Python
对Python中for复合语句的使用示例讲解
2018/11/01 Python
Python编程在flask中模拟进行Restful的CRUD操作
2018/12/28 Python
pycharm部署、配置anaconda环境的教程
2020/03/24 Python
python利用opencv保存、播放视频
2020/11/02 Python
CSS 3.0 结合video视频实现的创意开幕效果
2020/06/01 HTML / CSS
Europcar英国:英国汽车和货车租赁
2017/01/21 全球购物
会计系毕业个人自荐信格式
2013/09/23 职场文书
简历中自我评价怎么写
2014/02/12 职场文书
《傅雷家书》教学反思
2014/04/20 职场文书
人事经理岗位职责
2014/04/28 职场文书
组工干部对照检查材料
2014/08/25 职场文书
房屋租赁合同协议书范本
2014/10/19 职场文书
校长新学期寄语2016
2015/12/04 职场文书
导游词之西安骊山
2019/12/03 职场文书
Python如何利用正则表达式爬取网页信息及图片
2021/04/17 Python
pytorch通过训练结果的复现设置随机种子
2021/06/01 Python